MS C#.NET #20 |
Values are restored to registers, scheduler functions use register values and packet values. Each implementation of the scheduler function is stored in a Tcb data member, as a delegate type. C# language MS C#.NET version 1.0.3705 |
![]() |
using System;
using System.Collections.Specialized;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
internal class Scheduler
{
const bool TraceOn = false;
enum Kind {Device, Work}
enum Task {Idle, Worker, HandlerA, HandlerB, DeviceA, DeviceB}
const int MaxTasks = 10;
// Named bit masks to access 3 bit state value.
static readonly int RUNNING = 0;
static readonly int RUNNABLE = BitVector32.CreateMask();
static readonly int SUSPENDED = BitVector32.CreateMask(RUNNABLE);
static readonly int HELD = BitVector32.CreateMask(SUSPENDED);
static readonly int SUSPENDED_RUNNABLE = SUSPENDED | RUNNABLE;
static readonly int NOT_HELD = ~HELD;
class Packet
{
internal const int DATA_SIZE = 4;
internal Packet link;
internal Task id;
internal Kind kind;
internal byte a1 = 0;
internal byte[] a2 = new byte[DATA_SIZE];
internal Packet(Packet aLink, Task anId, Kind aKind)
{
link = aLink;
id = anId;
kind = aKind;
}
internal Packet AddTo(Packet aQueue)
{
Packet next, peek;
link = null;
if (aQueue == null) return this;
next = aQueue;
while ((peek = next.link) != null)
next = peek;
next.link = this;
return aQueue;
}
}
class Tcb
{
internal Tcb link;
internal Task id;
internal int pri;
internal Packet wkq;
internal int state;
internal object v1;
internal object v2;
internal Scheduler.Fn fn;
internal Tcb(Tcb aTcb, Task anId, int aPriority,
Packet aWorkQueue, object aV1, object aV2, Scheduler.Fn aFn
)
{
link = aTcb;
id = anId;
pri = aPriority;
wkq = aWorkQueue;
v1 = aV1;
v2 = aV2;
fn = aFn;
if (wkq == null)
state = SUSPENDED;
else
state = SUSPENDED_RUNNABLE;
}
}
static Tcb[] table = new Tcb[MaxTasks];
static Tcb list = null;
static Tcb currentTcb;
static Task currentId;
static object v1;
static object v2;
static int queueCount = 0;
static int holdCount = 0;
// Use Encoding to explicitly convert bytes to ascii chars
static ASCIIEncoding Encoding = new ASCIIEncoding();
static int Layout = 0;
// Timing
[DllImport("Kernel32.dll")]
private static extern bool QueryPerformanceCounter(
out long lpPerformanceCount);
// Timing
[DllImport("Kernel32.dll")]
private static extern bool QueryPerformanceFrequency(
out long lpFrequency);
static void Main(string[] args)
{
int count = 10000;
if (args.Length > 0)
count = Int32.Parse(args[0]);
// Timing
long startTicks, stopTicks, frequency;
QueryPerformanceCounter(out startTicks);
Packet wkq;
AddIdleTask(Task.Idle, 0, null, count);
wkq = new Packet(null, Task.Worker, Kind.Work);
wkq = new Packet(wkq, Task.Worker, Kind.Work);
AddWorkerTask(Task.Worker, 1000, wkq);
wkq = new Packet(null, Task.DeviceA, Kind.Device);
wkq = new Packet(wkq, Task.DeviceA, Kind.Device);
wkq = new Packet(wkq, Task.DeviceA, Kind.Device);
AddHandlerTask(Task.HandlerA, 2000, wkq);
wkq = new Packet(null, Task.DeviceB, Kind.Device);
wkq = new Packet(wkq, Task.DeviceB, Kind.Device);
wkq = new Packet(wkq, Task.DeviceB, Kind.Device);
AddHandlerTask(Task.HandlerB, 3000, wkq);
AddDeviceTask(Task.DeviceA, 4000, null);
AddDeviceTask(Task.DeviceB, 5000, null);
Schedule();
// Timing
QueryPerformanceCounter(out stopTicks);
QueryPerformanceFrequency(out frequency);
// Timing
FileInfo f = new FileInfo("testrun-times.xml");
StreamWriter sw = f.AppendText();
sw.Write("<ExternalStat>");
sw.Write("<size>{0}</size>", count);
sw.Write("<ticks>{0}</ticks>", stopTicks - startTicks);
sw.Write("<ticksPerSecond>{0}</ticksPerSecond>", frequency);
sw.WriteLine("</ExternalStat>");
sw.Flush();
sw.Close();
Console.WriteLine("QueueCount = {0}", queueCount);
Console.WriteLine("HoldCount = {0}", holdCount);
}
static void Trace(byte aByte)
{
--Layout;
if (Layout <= 0)
{
Console.WriteLine();
Layout = 50;
}
// need to explicitly convert byte to ascii char
byte[] b = new Byte[1];
b[0] = aByte;
char[] c = Encoding.GetChars(b);
Console.Write(c[0]);
}
static void AddDeviceTask(Task anId, int aPriority,
Packet aQueue)
{
AddTask(anId, aPriority, aQueue, null, null, new Fn(DeviceFn) );
}
static void AddHandlerTask(Task anId, int aPriority,
Packet aQueue)
{
AddTask(anId, aPriority, aQueue, null, null, new Fn(HandlerFn) );
}
static void AddIdleTask(Task anId, int aPriority,
Packet aQueue, int aCount)
{
AddRunningTask(anId, aPriority, aQueue, 1, aCount, new Fn(IdleFn) );
}
static void AddWorkerTask(Task anId, int aPriority,
Packet aWorkQueue)
{
AddTask(anId, aPriority, aWorkQueue,
Task.HandlerA, 0, new Fn(WorkerFn) );
}
static void AddTask(Task anId, int aPriority,
Packet aWorkQueue, object aV1, object aV2, Fn aFn)
{
currentTcb = new Tcb(list, anId, aPriority,
aWorkQueue, aV1, aV2, aFn);
list = currentTcb;
table[(int)anId] = currentTcb;
}
static void AddRunningTask(Task anId, int aPriority,
Packet aWorkQueue, object aV1, object aV2, Fn aFn)
{
AddTask(anId, aPriority, aWorkQueue, aV1, aV2, aFn);
currentTcb.state = RUNNING;
}
delegate Tcb Fn(Packet aPacket);
static Tcb DeviceFn(Packet aPacket)
{
if (aPacket == null)
{
if (v1 == null)
return SuspendCurrent();
Packet v = (Packet)v1;
v1 = null;
return Queue(v);
}
else
{
v1 = aPacket;
if (TraceOn) Trace(aPacket.a1);
return HoldCurrent();
}
}
static Tcb HandlerFn(Packet aPacket)
{
if (aPacket != null)
{
if (aPacket.kind == Kind.Work)
v1 = aPacket.AddTo((Packet)v1);
else
v2 = aPacket.AddTo((Packet)v2);
}
if (v1 != null)
{
byte count = ((Packet)v1).a1;
Packet v;
if (count < Packet.DATA_SIZE )
{
if (v2 != null )
{
v = (Packet)v2;
v2 = v.link;
v.a1 = ((Packet)v1).a2[count];
((Packet)v1).a1 = (byte)(count + 1);
return Queue(v);
}
}
else
{
v = (Packet)v1;
v1 = v.link;
return Queue(v);
}
}
return SuspendCurrent();
}
static Tcb IdleFn(Packet aPacket)
{
v2 = (int)v2 - 1;
if ((int)v2 == 0)
return HoldCurrent();
if ( ((int)v1 & 1) == 0 )
{
v1 = (int)v1 >> 1;
return Release(Task.DeviceA);
}
else
{
v1 = ((int)v1 >> 1) ^ 0xD008;
return Release(Task.DeviceB);
}
}
static Tcb WorkerFn(Packet aPacket)
{
if (aPacket == null)
{ return SuspendCurrent(); }
else
{
if ((Task)v1 == Task.HandlerA)
v1 = Task.HandlerB;
else
v1 = Task.HandlerA;
aPacket.id = (Task)v1;
aPacket.a1 = 0;
for (int i=0; i < Packet.DATA_SIZE; i++)
{
v2 = (int)v2 + 1;
if ((int)v2 > 26) v2 = 1;
aPacket.a2[i] = (byte)('A' + (int)v2 - 1);
}
return Queue(aPacket);
}
}
static Tcb HoldCurrent()
{
++holdCount;
currentTcb.state = currentTcb.state | HELD;
return currentTcb.link;
}
static Tcb Queue(Packet aPacket)
{
Tcb t = table[(int)aPacket.id];
if (t == null) return t;
queueCount++;
aPacket.link = null;
aPacket.id = currentId;
if (t.wkq == null )
{
t.wkq = aPacket;
t.state = t.state | RUNNABLE;
if (t.pri > currentTcb.pri) { return t; }
}
else
{
aPacket.AddTo(t.wkq);
}
return currentTcb;
}
static Tcb Release(Task anId)
{
Tcb t = table[(int)anId];
if (t == null) return t;
t.state = t.state & NOT_HELD;
if (t.pri > currentTcb.pri)
return t;
else
return currentTcb;
}
static void Schedule()
{
Packet packet;
currentTcb = list;
while (currentTcb != null)
{
if (currentTcb.state == SUSPENDED_RUNNABLE)
{
packet = currentTcb.wkq;
currentTcb.wkq = packet.link;
if (currentTcb.wkq == null)
currentTcb.state = RUNNING;
else
currentTcb.state = RUNNABLE;
}
else
{
packet = null;
}
if ((currentTcb.state & HELD) != 0 ||
(currentTcb.state == SUSPENDED))
{
currentTcb = currentTcb.link;
}
else
{
currentId = currentTcb.id;
v1 = currentTcb.v1;
v2 = currentTcb.v2;
if (TraceOn) Trace((byte)((int)'0' + currentId + 1));
Tcb nextTcb = currentTcb.fn(packet);
currentTcb.v1 = v1;
currentTcb.v2 = v2;
currentTcb = nextTcb;
}
}
}
static Tcb SuspendCurrent()
{
currentTcb.state = currentTcb.state | SUSPENDED;
return currentTcb;
}
}
|