/*
Object-oriented D version of the systems programming language benchmark
Author: M. J. Jordan Cambridge Computer Laboratory.
Modified by: M. Richards, Nov 1996
to be ANSI C and runnable on 64 bit machines + other minor changes
Modified by: M. Richards, 20 Oct 1998
made minor corrections to improve ANSI compliance (suggested
by David Levine)
Ported to Object-oriented D from the C# version.
This version has getters/setters and final classes.
*/
version (Tango) {
import tango.stdc.stdio: putchar, printf;
import Integer = tango.text.convert.Integer;
alias Integer.parse toInt;
} else {
import std.c.stdio: putchar, printf;
import std.conv: toInt;
}
// set this to true to see the debug output
const bool TraceOn = false;
final class Scheduler {
enum TaskId {
Idle,
Worker,
HandlerA,
HandlerB,
DeviceA,
DeviceB
}
const TaskId Idle = TaskId.Idle;
const TaskId Worker = TaskId.Worker;
const TaskId HandlerA = TaskId.HandlerA;
const TaskId HandlerB = TaskId.HandlerB;
const TaskId DeviceA = TaskId.DeviceA;
const TaskId DeviceB = TaskId.DeviceB;
private const int MaxTasks = 10;
private Tcb[MaxTasks] table;
private Tcb list = null;
private Tcb currentTcb;
private TaskId currentId;
int queueCount = 0;
int holdCount = 0;
void AddIdleTask(TaskId anId, int aPriority, Packet aWorkQueue, int aCount) {
AddRunningTask(anId, aPriority, aWorkQueue, new IdleTask(this, 1, aCount));
}
void AddWorkerTask(TaskId anId, int aPriority, Packet aWorkQueue) {
AddTask(anId, aPriority, aWorkQueue,
new WorkerTask(this, HandlerA, 0));
}
void AddHandlerTask(TaskId anId, int aPriority, Packet aWorkQueue) {
AddTask(anId, aPriority, aWorkQueue, new HandlerTask(this));
}
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[cast(int)anId] = currentTcb;
}
protected void AddRunningTask(TaskId anId, int aPriority, Packet aWorkQueue, ISchedulerTask aTask) {
AddTask(anId, aPriority, aWorkQueue, aTask);
currentTcb.SetRunning();
}
void Schedule() {
currentTcb = list;
while (currentTcb !is null) {
if (currentTcb.IsHeldOrSuspended())
currentTcb = currentTcb.Link;
else {
currentId = currentTcb.Id;
static if (TraceOn) Bench.Trace(cast(byte)(cast(int)'0' + currentId + 1));
currentTcb = currentTcb.Run();
}
}
}
Tcb Queue(Packet aPacket) {
Tcb t = table[cast(int)aPacket.Id];
if (t is null)
return t;
queueCount++;
aPacket.Link = null;
aPacket.Id = currentId;
return t.CheckPriorityAdd(currentTcb, aPacket);
}
Tcb Release(TaskId anId) {
Tcb t = table[cast(int)anId];
if (t is null)
return t;
t.NotHeld();
if (t.Priority > currentTcb.Priority)
return t;
else
return currentTcb;
}
Tcb HoldCurrent() {
++holdCount;
currentTcb.Held();
return currentTcb.Link;
}
Tcb SuspendCurrent() {
currentTcb.Suspended();
return currentTcb;
}
}
interface ISchedulerTask {
Tcb run(Packet aPacket);
}
/* try this instead
abstract class SchedulerTask {
abstract Tcb run(Packet aPacket);
}
*/
final class DeviceTask : ISchedulerTask {
Packet v1;
Scheduler s;
this(Scheduler aScheduler) {
s = aScheduler;
}
public Tcb run(Packet aPacket) {
if (aPacket is null) {
if (v1 is null)
return s.SuspendCurrent();
Packet v = v1;
v1 = null;
return s.Queue(v);
} else {
v1 = aPacket;
static if (TraceOn) Bench.Trace(aPacket.A1);
return s.HoldCurrent();
}
}
}
final class HandlerTask : ISchedulerTask {
Packet v1, v2;
Scheduler s;
this(Scheduler aScheduler) {
s = aScheduler;
}
public Tcb run(Packet aPacket) {
if (aPacket !is null) {
if (aPacket.Kind == Packet.Work)
v1 = aPacket.AddTo(v1);
else
v2 = aPacket.AddTo(v2);
}
if (v1 !is null) {
byte count = v1.A1;
Packet v;
if (count < Packet.dataSize) {
if (v2 !is null) {
v = v2;
v2 = v2.Link;
v.A1 = v1.A2[count];
v1.A1 = cast(byte)(count + 1);
return s.Queue(v);
}
} else {
v = v1;
v1 = v1.Link;
return s.Queue(v);
}
}
return s.SuspendCurrent();
}
}
final class IdleTask : ISchedulerTask {
int v1, v2;
Scheduler s;
this(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.DeviceA);
} else {
v1 = (v1 >> 1) ^ 0xD008;
return s.Release(Scheduler.DeviceB);
}
}
}
final class WorkerTask : ISchedulerTask {
Scheduler.TaskId v1;
int v2;
Scheduler s;
this(Scheduler aScheduler, Scheduler.TaskId aValue1, int aValue2) {
v1 = aValue1;
v2 = aValue2;
s = aScheduler;
}
public Tcb run(Packet aPacket) {
if (aPacket is null) {
return s.SuspendCurrent();
} else {
if (v1 == Scheduler.HandlerA)
v1 = Scheduler.HandlerB;
else
v1 = Scheduler.HandlerA;
aPacket.Id = v1;
aPacket.A1 = 0;
for (int i = 0; i < Packet.dataSize; i++) {
v2++;
if (v2 > 26)
v2 = 1;
aPacket.A2[i] = cast(byte)('A' + v2 - 1);
}
return s.Queue(aPacket);
}
}
}
final 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;
this(Tcb aTcb, Scheduler.TaskId anId, int aPriority, Packet aWorkQueue, ISchedulerTask aTask) {
link = aTcb;
id = anId;
pri = aPriority;
wkq = aWorkQueue;
task = aTask;
if (wkq is null)
state = SUSPENDED;
else
state = SUSPENDED_RUNNABLE;
}
///<value>The next Tcb or null.</value>
Tcb Link() {
return this.link;
}
Scheduler.TaskId Id() {
return this.id;
}
int Priority() {
return this.pri;
}
Tcb CheckPriorityAdd(Tcb aTask, Packet aPacket) {
if (wkq is null) {
wkq = aPacket;
state = state | RUNNABLE;
if (pri > aTask.Priority) { return this; }
} else {
wkq = aPacket.AddTo(wkq);
}
return aTask;
}
Tcb Run() {
Packet packet;
if (state == SUSPENDED_RUNNABLE) {
packet = wkq;
wkq = packet.Link;
if (wkq is null)
state = RUNNING;
else
state = RUNNABLE;
} else {
packet = null;
}
return task.run(packet);
}
// Named bit masks to access 3 bit state value.
static const int RUNNING = 0;
static const int RUNNABLE = 1;
static const int SUSPENDED = 2;
static const int HELD = 4;
static const int SUSPENDED_RUNNABLE = SUSPENDED | RUNNABLE;
static const int NOT_HELD = ~HELD;
void SetRunning() {
state = RUNNING;
}
void Suspended() {
state = state | SUSPENDED;
}
void Held() {
state = state | HELD;
}
void NotHeld() {
state = state & NOT_HELD;
}
bool IsHeldOrSuspended() {
return (state & HELD) != 0 || (state == SUSPENDED);
}
}
final class Packet {
enum Kinds { Device, Work };
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[dataSize] a2;
// Device & Work alias the enum Kinds values.
const Kinds Device = Kinds.Device;
const Kinds Work = Kinds.Work;
this(Packet aLink, Scheduler.TaskId anId, Kinds aKind) {
link = aLink;
id = anId;
kind = aKind;
}
Packet AddTo(Packet aQueue) {
Packet next, peek;
link = null;
if (aQueue is null)
return this;
next = aQueue;
while ((peek = next.Link) !is null)
next = peek;
next.Link = this;
return aQueue;
}
Packet Link() {
return this.link;
}
void Link(Packet value) {
return this.link = value;
}
Scheduler.TaskId Id() {
return this.id;
}
void Id(Scheduler.TaskId value) {
this.id = value;
}
Kinds Kind() {
return this.kind;
}
byte A1() {
return a1;
}
void A1(byte value) {
this.a1 = value;
}
byte[] A2() {
return this.a2;
}
}
final class Bench {
static int Layout = 0;
static void Trace(byte aByte) {
--Layout;
if (Layout <= 0) {
putchar('\n');
Layout = 50;
}
putchar(aByte);
}
}
void main(char[][] args) {
int count = 10000;
if (args.length > 1)
count = toInt(args[1]);
Scheduler s = new Scheduler();
Packet wkq;
s.AddIdleTask(Scheduler.Idle, 0, null, count);
wkq = new Packet(null, Scheduler.Worker, Packet.Work);
wkq = new Packet(wkq, Scheduler.Worker, Packet.Work);
s.AddWorkerTask(Scheduler.Worker, 1000, wkq);
wkq = new Packet(null, Scheduler.DeviceA, Packet.Device);
wkq = new Packet(wkq, Scheduler.DeviceA, Packet.Device);
wkq = new Packet(wkq, Scheduler.DeviceA, Packet.Device);
s.AddHandlerTask(Scheduler.HandlerA, 2000, wkq);
wkq = new Packet(null, Scheduler.DeviceB, Packet.Device);
wkq = new Packet(wkq, Scheduler.DeviceB, Packet.Device);
wkq = new Packet(wkq, Scheduler.DeviceB, Packet.Device);
s.AddHandlerTask(Scheduler.HandlerB, 3000, wkq);
s.AddDeviceTask(Scheduler.DeviceA, 4000, null);
s.AddDeviceTask(Scheduler.DeviceB, 5000, null);
s.Schedule();
printf("QueueCount = %d\n", s.queueCount);
printf("HoldCount = %d\n", s.holdCount);
}