using System;
using System.Collections.Specialized;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
internal class Bench
{
const bool TraceOn = false;
// 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);
Scheduler s = new Scheduler(TraceOn);
Packet wkq;
s.AddIdleTask(Scheduler.TaskId.Idle, 0, null, count);
wkq = new Packet(null, Scheduler.TaskId.Worker, Packet.Kinds.Work);
wkq = new Packet(wkq, Scheduler.TaskId.Worker, Packet.Kinds.Work);
s.AddWorkerTask(Scheduler.TaskId.Worker, 1000, wkq);
wkq = new Packet(null, Scheduler.TaskId.DeviceA, Packet.Kinds.Device);
wkq = new Packet(wkq, Scheduler.TaskId.DeviceA, Packet.Kinds.Device);
wkq = new Packet(wkq, Scheduler.TaskId.DeviceA, Packet.Kinds.Device);
s.AddHandlerTask(Scheduler.TaskId.HandlerA, 2000, wkq);
wkq = new Packet(null, Scheduler.TaskId.DeviceB, Packet.Kinds.Device);
wkq = new Packet(wkq, Scheduler.TaskId.DeviceB, Packet.Kinds.Device);
wkq = new Packet(wkq, Scheduler.TaskId.DeviceB, Packet.Kinds.Device);
s.AddHandlerTask(Scheduler.TaskId.HandlerB, 3000, wkq);
s.AddDeviceTask(Scheduler.TaskId.DeviceA, 4000, null);
s.AddDeviceTask(Scheduler.TaskId.DeviceB, 5000, null);
s.Schedule();
// Timing
QueryPerformanceCounter(out stopTicks);
QueryPerformanceFrequency(out frequency);
// Timing
String ss = "";
FileInfo f = new FileInfo("testrun-times.xml");
if (f.Exists){
StreamReader sr = f.OpenText();
ss = sr.ReadToEnd();
sr.Close();
}
StreamWriter sw = new StreamWriter("testrun-times.xml");
sw.Write(ss);
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}", s.queueCount);
Console.WriteLine("HoldCount = {0}", s.holdCount);
}
internal 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]);
}
}
internal class Scheduler
{
private Tcb[] table = new Tcb[MaxTasks];
private Tcb list = null;
private Tcb currentTcb;
private TaskId currentId;
internal int queueCount = 0;
internal int holdCount = 0;
internal bool traceOn = false;
internal enum TaskId
{
Idle, Worker, HandlerA, HandlerB,
DeviceA, DeviceB}
protected const int MaxTasks = 10;
internal Scheduler(bool trace)
{
traceOn = trace;
}
internal void AddIdleTask(TaskId anId, int aPriority,
Packet aWorkQueue, int aCount)
{
AddRunningTask(anId, aPriority, aWorkQueue,
new IdleTask(this, 1, aCount) );
}
internal void AddWorkerTask(TaskId anId, int aPriority,
Packet aWorkQueue)
{
AddTask(anId, aPriority, aWorkQueue,
new WorkerTask(this, TaskId.HandlerA, 0) );
}
internal void AddHandlerTask(TaskId anId, int aPriority,
Packet aWorkQueue)
{
AddTask(anId, aPriority, aWorkQueue,
new HandlerTask(this) );
}
internal void AddDeviceTask(TaskId anId, int aPriority,
Packet aWorkQueue
)
{
AddTask(anId, aPriority, aWorkQueue,
new DeviceTask(this) );
}
protected void AddTask(TaskId anId, int aPriority,
Packet aWorkQueue, ISchedulerTask aTask)
{
currentTcb = new Tcb(list, anId, aPriority, aWorkQueue, aTask);
list = currentTcb;
table[(int)anId] = currentTcb;
}
protected void AddRunningTask(TaskId anId, int aPriority,
Packet aWorkQueue, ISchedulerTask aTask)
{
AddTask(anId, aPriority, aWorkQueue, aTask);
currentTcb.SetRunning();
}
internal void Schedule()
{
currentTcb = list;
while (currentTcb != null)
{
if (currentTcb.IsHeldOrSuspended())
currentTcb = currentTcb.Link;
else
{
currentId = currentTcb.Id;
//if (traceOn) Bench.Trace((byte)((int)'0' + currentId + 1));
currentTcb = currentTcb.Run();
}
}
}
internal Tcb Queue(Packet aPacket)
{
Tcb t = table[(int)aPacket.Id];
if (t == null) return t;
queueCount++;
aPacket.Link = null;
aPacket.Id = currentId;
return t.CheckPriorityAdd(currentTcb, aPacket);
}
internal Tcb Release(TaskId anId)
{
Tcb t = table[(int)anId];
if (t == null) return t;
t.NotHeld();
if (t.Priority > currentTcb.Priority)
return t;
else
return currentTcb;
}
internal Tcb HoldCurrent()
{
++holdCount;
currentTcb.Held();
return currentTcb.Link;
}
internal Tcb SuspendCurrent()
{
currentTcb.Suspended();
return currentTcb;
}
}
internal interface ISchedulerTask
{
Tcb run(Packet aPacket);
}
class DeviceTask : ISchedulerTask
{
Packet v1;
Scheduler s;
public DeviceTask(Scheduler aScheduler)
{
s = aScheduler;
}
public Tcb run(Packet aPacket)
{
if (aPacket == null)
{
if (v1 == null)
return s.SuspendCurrent();
Packet v = v1;
v1 = null;
return s.Queue(v);
}
else
{
v1 = aPacket;
//if (s.traceOn) Bench.Trace(aPacket.A1);
return s.HoldCurrent();
}
}
}
class HandlerTask : ISchedulerTask
{
Packet v1, v2;
Scheduler s;
public HandlerTask(Scheduler aScheduler)
{
s = aScheduler;
}
public Tcb run(Packet aPacket)
{
if (aPacket != null)
{
if (aPacket.Kind == Packet.Kinds.Work)
v1 = aPacket.AddTo(v1);
else
v2 = aPacket.AddTo(v2);
}
if (v1 != null)
{
byte count = v1.A1;
Packet v;
if (count < Packet.dataSize )
{
if (v2 != null )
{
v = v2;
v2 = v2.Link;
v.A1 = v1.A2[count];
v1.A1 = (byte)(count + 1);
return s.Queue(v);
}
}
else
{
v = v1;
v1 = v1.Link;
return s.Queue(v);
}
}
return s.SuspendCurrent();
}
}
class IdleTask : ISchedulerTask
{
int v1, v2;
Scheduler s;
public IdleTask(Scheduler aScheduler, int aValue1, int aValue2)
{
v1 = aValue1;
v2 = aValue2;
s = aScheduler;
}
public Tcb run(Packet aPacket)
{
v2--;
if (v2 == 0)
return s.HoldCurrent();
if ( (v1 & 1) == 0 )
{
v1 = v1 >> 1;
return s.Release(Scheduler.TaskId.DeviceA);
}
else
{
v1 = (v1 >> 1) ^ 0xD008;
return s.Release(Scheduler.TaskId.DeviceB);
}
}
}
class WorkerTask : ISchedulerTask
{
Scheduler.TaskId v1;
int v2;
Scheduler s;
public WorkerTask(Scheduler aScheduler, Scheduler.TaskId aValue1, int aValue2)
{
v1 = aValue1;
v2 = aValue2;
s = aScheduler;
}
public Tcb run(Packet aPacket)
{
if (aPacket == null)
{ return s.SuspendCurrent(); }
else
{
if (v1 == Scheduler.TaskId.HandlerA)
v1 = Scheduler.TaskId.HandlerB;
else
v1 = Scheduler.TaskId.HandlerA;
aPacket.Id = v1;
aPacket.A1 = 0;
for (int i=0; i < Packet.dataSize; i++)
{
v2++;
if (v2 > 26) v2 = 1;
aPacket.A2[i] = (byte)('A' + v2 - 1);
}
return s.Queue(aPacket);
}
}
}
internal class Tcb
{
// Variables from spec
private Tcb link; // pointer to another tcb or nil
private Scheduler.TaskId id; // identifier (a small integer)
private int pri; // priority (a positive integer)
private Packet wkq; // list of Packets in the tasks work queue
private int state; // a 3 bit value giving the state
private ISchedulerTask task;
internal Tcb(Tcb aTcb, Scheduler.TaskId anId, int aPriority,
Packet aWorkQueue, ISchedulerTask aTask
)
{
link = aTcb;
id = anId;
pri = aPriority;
wkq = aWorkQueue;
task = aTask;
if (wkq == null)
state = SUSPENDED;
else
state = SUSPENDED_RUNNABLE;
}
internal Tcb Link
{
get { return link; }
}
internal Scheduler.TaskId Id
{
get { return id; }
}
internal int Priority
{
get { return pri; }
}
internal Tcb CheckPriorityAdd(Tcb aTask, Packet aPacket)
{
if (wkq == null )
{
wkq = aPacket;
state = state | RUNNABLE;
if (pri > aTask.Priority) { return this; }
}
else
{
wkq = aPacket.AddTo(wkq);
}
return aTask;
}
internal Tcb Run()
{
Packet packet;
if (state == SUSPENDED_RUNNABLE)
{
packet = wkq;
wkq = packet.Link;
if (wkq == null)
state = RUNNING;
else
state = RUNNABLE;
}
else
{
packet = null;
}
return task.run(packet);
}
// Named bit masks to access 3 bit state value.
private static readonly int RUNNING = 0;
private static readonly int RUNNABLE = BitVector32.CreateMask();
private static readonly int SUSPENDED = BitVector32.CreateMask(RUNNABLE);
private static readonly int HELD = BitVector32.CreateMask(SUSPENDED);
private static readonly int SUSPENDED_RUNNABLE = SUSPENDED | RUNNABLE;
private static readonly int NOT_HELD = ~HELD;
internal void SetRunning()
{
state = RUNNING;
}
internal void Suspended()
{
state = state | SUSPENDED;
}
internal void Held()
{
state = state | HELD;
}
internal void NotHeld()
{
state = state & NOT_HELD;
}
internal bool IsHeldOrSuspended()
{
return (state & HELD) != 0 || (state == SUSPENDED);
}
}
internal class Packet
{
internal const int dataSize = 4;
// Variables from spec
private Packet link; // pointer to the next Packet or nil
private Scheduler.TaskId id; // task or device that sent the packet
private Kinds kind; // part of the message
private byte a1 = 0; // part of the message
private byte[] a2 = new byte[dataSize];
// Device & Work alias the enum Kinds values.
internal enum Kinds {Device, Work};
internal Packet(Packet aLink, Scheduler.TaskId anId, Kinds 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;
}
internal Packet Link
{
get { return link; }
set { link = value; }
}
internal Scheduler.TaskId Id
{
get { return id; }
set { id = value; }
}
internal Kinds Kind
{
get { return kind; }
}
internal byte A1
{
get { return a1; }
set { a1 = value; }
}
internal byte[] A2
{
get { return a2; }
}
}
|