Skip to content

Instantly share code, notes, and snippets.

@timo
Last active December 21, 2015 02:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timo/6236138 to your computer and use it in GitHub Desktop.
Save timo/6236138 to your computer and use it in GitHub Desktop.
working version of richards for nqp, nonworking version of richards for perl6.
my int $COUNT := 1000;
my int $DATA_SIZE := 4;
my int $ID_IDLE := 0;
my int $ID_WORKER := 1;
my int $ID_HANDLER_A := 2;
my int $ID_HANDLER_B := 3;
my int $ID_DEVICE_A := 4;
my int $ID_DEVICE_B := 5;
my int $NUMBER_OF_IDS := 6;
my int $KIND_DEVICE := 0;
my int $KIND_WORK := 1;
my int $STATE_RUNNING := 0;
my int $STATE_RUNNABLE := 1;
my int $STATE_SUSPENDED := 2;
my int $STATE_HELD := 4;
my int $STATE_SUSPENDED_RUNNABLE := $STATE_SUSPENDED +| $STATE_RUNNABLE;
my int $EXPECTED_QUEUE_COUNT := 2322;
my int $EXPECTED_HOLD_COUNT := 928;
my int $packet_ctr;
sub run_richards() {
my $sched := Scheduler.new();
$sched.addIdleTask($ID_IDLE, 0, NQPMu, $COUNT);
my $q := Packet.new(NQPMu, $ID_WORKER, $KIND_WORK);
$q := Packet.new($q, $ID_WORKER, $KIND_WORK);
$sched.addWorkerTask($ID_WORKER, 1000, $q);
$q := Packet.new(NQPMu, $ID_DEVICE_A, $KIND_DEVICE);
$q := Packet.new($q, $ID_DEVICE_A, $KIND_DEVICE);
$q := Packet.new($q, $ID_DEVICE_A, $KIND_DEVICE);
$sched.addHandlerTask($ID_HANDLER_A, 2000, $q);
$q := Packet.new(NQPMu, $ID_DEVICE_B, $KIND_DEVICE);
$q := Packet.new($q, $ID_DEVICE_B, $KIND_DEVICE);
$q := Packet.new($q, $ID_DEVICE_B, $KIND_DEVICE);
$sched.addHandlerTask($ID_HANDLER_B, 3000, $q);
$sched.addDeviceTask($ID_DEVICE_A, 4000, NQPMu);
$sched.addDeviceTask($ID_DEVICE_B, 5000, NQPMu);
$sched.schedule();
if $COUNT == 1000 {
$sched.confirm_counts($EXPECTED_QUEUE_COUNT, $EXPECTED_HOLD_COUNT);
}
}
class Scheduler {
has $!queue_count;
has $!hold_count;
has @!blocks;
has $!head;
has $!current_tcb;
has $!current_id;
method new() {
my $new := self.CREATE;
my @l := nqp::list();
nqp::setelems(@l, $NUMBER_OF_IDS);
nqp::bindattr($new, Scheduler, '@!blocks', @l);
nqp::bindattr($new, Scheduler, '$!head', NQPMu);
nqp::bindattr($new, Scheduler, '$!current_tcb', NQPMu);
nqp::bindattr($new, Scheduler, '$!current_id', NQPMu);
$new;
}
method addIdleTask($id, $prio, $queue, $count) {
self.addRunningTask($id, $prio, $queue, IdleTask.new(self, 1, $count));
}
method addWorkerTask($id, $prio, $queue) {
self.addTask($id, $prio, $queue, WorkerTask.new(self, $ID_HANDLER_A, 0));
}
method addHandlerTask($id, $prio, $queue) {
self.addTask($id, $prio, $queue, HandlerTask.new(self));
}
method addDeviceTask($id, $prio, $queue) {
self.addTask($id, $prio, $queue, DeviceTask.new(self))
}
method addRunningTask($id, $prio, $queue, $task) {
self.addTask($id, $prio, $queue, $task);
$!current_tcb.set_running;
}
method addTask($id, $prio, $queue, $task) {
$!current_tcb := TaskControlBlock.new($!head, $id, $prio, $queue, $task);
$!head := $!current_tcb;
@!blocks[$id] := $!current_tcb;
}
method suspend_current() {
$!current_tcb.mark_as_suspended();
return $!current_tcb;
}
method hold_current() {
$!hold_count := $!hold_count + 1;
$!current_tcb.mark_as_held();
return $!current_tcb.link;
}
method release($id) {
my $tcb := @!blocks[$id];
if $tcb =:= NQPMu {
return $tcb
}
$tcb.mark_as_not_held;
if $tcb.prio > $!current_tcb.prio {
return $tcb
} else {
return $!current_tcb;
}
}
method queue($packet) {
my $t := @!blocks[$packet.id];
if $t =:= NQPMu {
return $t
}
$!queue_count := $!queue_count + 1;
$packet.link(NQPMu);
$packet.id($!current_id);
return $t.check_priority_add($!current_tcb, $packet);
}
method schedule() {
$!current_tcb := $!head;
while !($!current_tcb =:= NQPMu) {
if $!current_tcb.is_held_or_suspended {
$!current_tcb := $!current_tcb.link
} else {
$!current_id := $!current_tcb.id;
$!current_tcb := $!current_tcb.run;
}
}
}
method confirm_counts($queue, $hold) {
if $!queue_count != $queue || $!hold_count != $hold {
nqp::die("counts mismatched: $!queue_count vs. $queue and $!hold_count vs. $hold");
}
}
}
class TaskControlBlock {
has $!link;
has $!id;
has $!prio;
has $!queue;
has $!task;
has $!state;
method new($link, $id, $prio, $queue, $task) {
my $new := self.CREATE();
nqp::bindattr($new, TaskControlBlock, '$!link', $link);
nqp::bindattr($new, TaskControlBlock, '$!id', $id);
nqp::bindattr($new, TaskControlBlock, '$!prio', $prio);
nqp::bindattr($new, TaskControlBlock, '$!queue', $queue);
nqp::bindattr($new, TaskControlBlock, '$!task', $task);
my $state;
if $queue =:= NQPMu {
$state := $STATE_SUSPENDED;
} else {
$state := $STATE_SUSPENDED_RUNNABLE;
}
nqp::bindattr($new, TaskControlBlock, '$!state', $state);
$new;
}
method run() {
my $packet;
if $!state == $STATE_SUSPENDED_RUNNABLE {
$packet := $!queue;
$!queue := $packet.link;
if $!queue =:= NQPMu {
$!state := $STATE_RUNNING;
} else {
$!state := $STATE_RUNNABLE;
}
} else {
$packet := NQPMu
}
return $!task.run($packet);
}
method link(*@link) {
if +@link {
$!link := @link[0];
}
$!link;
}
method id(*@id) {
if +@id {
$!id := @id[0];
}
$!id;
}
method prio(*@prio) {
if +@prio {
$!prio := @prio[0];
}
$!prio;
}
method set_running() {
$!state := $STATE_RUNNING;
}
method mark_as_suspended() {
$!state := $!state +| $STATE_SUSPENDED;
}
method is_held_or_suspended() {
($!state +& $STATE_HELD) || ($!state == $STATE_SUSPENDED)
}
method mark_as_not_held() {
if $!state +& $STATE_HELD {
$!state := $!state - $STATE_HELD;
}
}
method mark_as_held() {
$!state := $!state +| $STATE_HELD;
}
method mark_as_runnable() {
$!state := $!state +| $STATE_RUNNABLE;
}
method check_priority_add($task, $packet) {
if $!queue =:= NQPMu {
$!queue := $packet;
self.mark_as_runnable;
if $!prio > $task.prio {
return self
}
} else {
$!queue := $packet.add_to($!queue);
}
return $task;
}
}
class IdleTask {
has $!scheduler;
has $!v1;
has $!count;
method new($scheduler, $v1, $count) {
my $new := self.CREATE();
nqp::bindattr($new, IdleTask, '$!scheduler', $scheduler);
nqp::bindattr($new, IdleTask, '$!v1', $v1);
nqp::bindattr($new, IdleTask, '$!count', $count);
$new;
}
method run($packet) {
$!count := $!count - 1;
if $!count == 0 {
return $!scheduler.hold_current()
} else {
if ($!v1 +& 1) == 0 {
$!v1 := nqp::bitshiftr_i($!v1, 1);
return $!scheduler.release($ID_DEVICE_A);
} else {
$!v1 := nqp::bitxor_i(nqp::bitshiftr_i($!v1, 1), 0xD008);
return $!scheduler.release($ID_DEVICE_B);
}
}
}
}
class DeviceTask {
has $!scheduler;
has $!v1;
method new($scheduler) {
my $new := self.CREATE();
nqp::bindattr($new, DeviceTask, '$!scheduler', $scheduler);
nqp::bindattr($new, DeviceTask, '$!v1', NQPMu);
$new;
}
method run($packet) {
if $packet =:= NQPMu {
if $!v1 =:= NQPMu {
return $!scheduler.suspend_current();
} else {
my $v := $!v1;
$!v1 := NQPMu;
return $!scheduler.queue($v);
}
} else {
$!v1 := $packet;
return $!scheduler.hold_current();
}
}
}
class WorkerTask {
has $!scheduler;
has $!v1;
has $!v2;
method new($scheduler, $v1, $v2) {
my $new := self.CREATE();
nqp::bindattr($new, WorkerTask, '$!scheduler', $scheduler);
nqp::bindattr($new, WorkerTask, '$!v1', $v1);
nqp::bindattr($new, WorkerTask, '$!v2', $v2);
$new;
}
method run($packet) {
if $packet =:= NQPMu {
return $!scheduler.suspend_current();
} else {
if ($!v1 == $ID_HANDLER_A) {
$!v1 := $ID_HANDLER_B
} else {
$!v1 := $ID_HANDLER_A
}
$packet.id($!v1);
$packet.a1(0);
my int $i := 0;
while $i < $DATA_SIZE {
$!v2 := $!v2 + 1;
if $!v2 > 26 {
$!v2 := 1
}
$packet.a2[$i] := $!v2;
$i := $i + 1;
}
return $!scheduler.queue($packet);
}
}
}
class HandlerTask {
has $!scheduler;
has $!v1;
has $!v2;
method new($scheduler) {
my $new := self.CREATE();
nqp::bindattr($new, HandlerTask, '$!scheduler', $scheduler);
nqp::bindattr($new, HandlerTask, '$!v1', NQPMu);
nqp::bindattr($new, HandlerTask, '$!v2', NQPMu);
$new;
}
method run($packet) {
if !($packet =:= NQPMu) {
if $packet.kind == $KIND_WORK {
$!v1 := $packet.add_to($!v1);
} else {
$!v2 := $packet.add_to($!v2);
}
}
if !($!v1 =:= NQPMu) {
my int $count := $!v1.a1;
my $v;
if $count < $DATA_SIZE {
if !($!v2 =:= NQPMu) {
$v := $!v2;
$!v2 := $!v2.link;
$v.a1($!v1.a2[$count]);
$!v1.a1($count + 1);
return $!scheduler.queue($v);
}
} else {
$v := $!v1;
$!v1 := $!v1.link;
return $!scheduler.queue($v);
}
}
$!scheduler.suspend_current();
}
}
class Packet {
has $!link;
has $!id;
has $!kind;
has $!a1;
has @!a2;
has $!name;
method new($link, int $id, int $kind) {
my $new := self.CREATE();
nqp::bindattr($new, Packet, '$!link', $link);
nqp::bindattr($new, Packet, '$!id', $id);
nqp::bindattr($new, Packet, '$!kind', $kind);
nqp::bindattr($new, Packet, '$!a1', 0);
my @a2 := nqp::list_i();
nqp::setelems(@a2, $DATA_SIZE);
nqp::bindattr($new, Packet, '@!a2', @a2);
nqp::bindattr($new, Packet, '$!name', $packet_ctr);
$packet_ctr := $packet_ctr + 1;
$new;
}
method id(*@id) {
if +@id {
$!id := @id[0];
}
$!id;
}
method a1(*@a1) {
if +@a1 {
$!a1 := @a1[0];
}
$!a1;
}
method a2(*@a2) {
if +@a2 {
@!a2 := @a2[0];
}
@!a2;
}
method link(*@link) {
if +@link {
$!link := @link[0];
}
$!link;
}
method kind(*@kind) {
if +@kind {
$!kind := @kind[0];
}
$!kind;
}
method add_to($queue) {
$!link := NQPMu;
if $queue =:= NQPMu {
return self;
}
my $peek := $queue;
my $next := $queue;
while !($peek =:= NQPMu) {
$next := $peek;
$peek := $next.link;
}
$next.link(self);
$queue;
}
method Str() {
if $!link =:= NQPMu {
return "Packet($!name) -|";
} else {
return "Packet($!name) - " ~ $!link.Str;
}
}
}
run_richards();
constant COUNT = 1000;
constant DATA_SIZE = 4;
constant ID_IDLE = 0;
constant ID_WORKER = 1;
constant ID_HANDLER_A = 2;
constant ID_HANDLER_B = 3;
constant ID_DEVICE_A = 4;
constant ID_DEVICE_B = 5;
constant NUMBER_OF_IDS = 6;
constant KIND_DEVICE = 0;
constant KIND_WORK = 1;
constant STATE_RUNNING = 0;
constant STATE_RUNNABLE = 1;
constant STATE_SUSPENDED = 2;
constant STATE_HELD = 4;
constant STATE_SUSPENDED_RUNNABLE = STATE_SUSPENDED +| STATE_RUNNABLE;
constant EXPECTED_QUEUE_COUNT = 2322;
constant EXPECTED_HOLD_COUNT = 928;
class Scheduler { ... }
class TaskControlBlock { ... }
class IdleTask { ... }
class DeviceTask { ... }
class WorkerTask { ... }
class HandlerTask { ... }
class Packet { ... }
sub run_richards() {
my $sched = Scheduler.new();
$sched.addIdleTask(ID_IDLE, 0, Nil, COUNT);
my $q = Packet.new(Nil, ID_WORKER, KIND_WORK);
$q = Packet.new($q, ID_WORKER, KIND_WORK);
$sched.addWorkerTask(ID_WORKER, 1000, $q);
$q = Packet.new(Nil, ID_DEVICE_A, KIND_DEVICE);
$q = Packet.new($q, ID_DEVICE_A, KIND_DEVICE);
$q = Packet.new($q, ID_DEVICE_A, KIND_DEVICE);
$sched.addHandlerTask(ID_HANDLER_A, 2000, $q);
$q = Packet.new(Nil, ID_DEVICE_B, KIND_DEVICE);
$q = Packet.new($q, ID_DEVICE_B, KIND_DEVICE);
$q = Packet.new($q, ID_DEVICE_B, KIND_DEVICE);
$sched.addHandlerTask(ID_HANDLER_B, 3000, $q);
$sched.addDeviceTask(ID_DEVICE_A, 4000, Nil);
$sched.addDeviceTask(ID_DEVICE_B, 5000, Nil);
$sched.schedule();
if COUNT == 1000 {
$sched.confirm_counts(EXPECTED_QUEUE_COUNT, EXPECTED_HOLD_COUNT);
}
}
class Scheduler {
has $.queue_count;
has $.hold_count;
has @.blocks;
has $.head;
has $.current_tcb;
has $.current_id;
method addIdleTask($id, $prio, $queue, $count) {
self.addRunningTask: $id, $prio, $queue,
IdleTask.new(:scheduler(self), :v1(1), :$count);
}
method addWorkerTask($id, $prio, $queue) {
self.addTask: $id, $prio, $queue,
WorkerTask.new(self, ID_HANDLER_A, 0);
}
method addHandlerTask($id, $prio, $queue) {
self.addTask: $id, $prio, $queue,
HandlerTask.new(self);
}
method addDeviceTask($id, $prio, $queue) {
self.addTask: $id, $prio, $queue,
DeviceTask.new(self)
}
method addRunningTask($id, $prio, $queue, $task) {
self.addTask: $id, $prio, $queue, $task;
$!current_tcb.set_running;
}
method addTask($id, $prio, $queue, $task) {
$!current_tcb = TaskControlBlock.new: :$!head, :$id, :$prio, :$queue, :$task;
$!head = $!current_tcb;
@!blocks[$id] = $!current_tcb;
}
method suspend_current() {
$!current_tcb.mark_as_suspended();
return $!current_tcb;
}
method hold_current() {
$!hold_count = $!hold_count + 1;
$!current_tcb.mark_as_held();
return $!current_tcb.link;
}
method release($id) {
my $tcb = @!blocks[$id];
unless $tcb.defined {
return $tcb
}
$tcb.mark_as_not_held;
if $tcb.prio > $!current_tcb.prio {
return $tcb
} else {
return $!current_tcb;
}
}
method queue($packet) {
my $t = @!blocks[$packet.id];
unless $t.defined {
return $t
}
$!queue_count = $!queue_count + 1;
$packet.link = Nil;
$packet.id = $!current_id;
return $t.check_priority_add($!current_tcb, $packet);
}
method schedule() {
$!current_tcb = $!head;
while $!current_tcb.defined {
if $!current_tcb.is_held_or_suspended {
$!current_tcb = $!current_tcb.link
} else {
$!current_id = $!current_tcb.id;
$!current_tcb = $!current_tcb.run;
}
}
}
method confirm_counts($queue, $hold) {
if $!queue_count != $queue || $!hold_count != $hold {
die("counts mismatched: $!queue_count vs. $queue and $!hold_count vs. $hold");
}
}
}
class TaskControlBlock {
has $.link;
has $.id;
has $.prio;
has $.queue;
has $.task;
has $.state = $!queue.defined ?? STATE_SUSPENDED_RUNNABLE !! STATE_SUSPENDED;
method run() {
my $packet;
if $!state == STATE_SUSPENDED_RUNNABLE {
$packet = $!queue;
$!queue = $packet.link;
if $!queue.defined {
$!state = STATE_RUNNABLE;
} else {
$!state = STATE_RUNNING;
}
} else {
$packet = Nil
}
return $!task.run($packet);
}
method set_running() {
$!state = STATE_RUNNING;
}
method mark_as_suspended() {
$!state = $!state +| STATE_SUSPENDED;
}
method is_held_or_suspended() {
($!state +& STATE_HELD) || ($!state == STATE_SUSPENDED)
}
method mark_as_not_held() {
$!state = $!state - STATE_HELD;
}
method mark_as_held() {
$!state = $!state +| STATE_HELD;
}
method mark_as_runnable() {
$!state = $!state +| STATE_RUNNABLE;
}
method check_priority_add($task, $packet) {
if $!queue.defined {
$!queue = $packet.add_to($!queue);
} else {
$!queue = $packet;
self.mark_as_runnable;
if $!prio > $task.prio {
return self
}
}
return $task;
}
}
class IdleTask {
has $.scheduler;
has $.v1;
has $.count;
method run($packet) {
$!count = $!count - 1;
if $!count == 0 {
return $!scheduler.hold_current()
} else {
if ($!v1 +& 1) == 0 {
$!v1 = $!v1 +> 1;
return $!scheduler.release(ID_DEVICE_A);
} else {
$!v1 = ($!v1 +> 1) +^ 0xD008;
return $!scheduler.release(ID_DEVICE_B);
}
}
}
}
class DeviceTask {
has $.scheduler;
has $.v1;
method new($scheduler) {
self.bless: *, :$scheduler;
}
method run($packet) {
if $packet.defined {
$!v1 = $packet;
return $!scheduler.hold_current();
} else {
if $!v1.defined {
my $v = $!v1;
$!v1 = Mu;
return $!scheduler.queue($v);
} else {
return $!scheduler.suspend_current();
}
}
}
}
class WorkerTask {
has $.scheduler;
has $.v1;
has $.v2;
method new($scheduler, $v1, $v2) {
self.bless: *, :$scheduler, :$v1, :$v2;
}
method run($packet) {
if $packet.defined {
if ($!v1 == ID_HANDLER_A) {
$!v1 = ID_HANDLER_B
} else {
$!v1 = ID_HANDLER_A
}
$packet.id = $!v1;
$packet.a1 = 0;
my int $i = 0;
while $i < DATA_SIZE {
$!v2 = $!v2 + 1;
if $!v2 > 26 {
$!v2 = 1
}
$packet.a2[$i] = $!v2;
$i = $i + 1;
}
return $!scheduler.queue($packet);
} else {
return $!scheduler.suspend_current();
}
}
}
class HandlerTask {
has $.scheduler;
has $.v1;
has $.v2;
method new($scheduler) {
self.bless: *, :$scheduler;
}
method run($packet) {
if $packet.defined {
if $packet.kind == KIND_WORK {
$!v1 = $packet.add_to($!v1);
} else {
$!v2 = $packet.add_to($!v2);
}
}
if $!v1.defined {
my int $count = $!v1.a1;
my $v;
if $count < DATA_SIZE {
if $!v2.defined {
$v = $!v2;
$!v2 = $!v2.link;
$v.a1 = $!v1.a2[$count];
$!v1.a1 = $count + 1;
return $!scheduler.queue($v);
}
} else {
$v = $!v1;
$!v1 = $!v1.link;
return $!scheduler.queue($v);
}
}
$!scheduler.suspend_current();
}
}
class Packet is rw {
has $.link;
has $.id;
has $.kind;
has $.a1;
has @.a2;
has $.name;
method new($link, $id, $kind) {
self.bless: *, :$link, :$id, :$kind;
}
method add_to($queue) {
$!link = Nil;
unless $queue.defined {
return self;
}
($queue, { $^q.link } ...^ Mu)[*-1].link = self;
$queue;
}
method Str() {
if $!link.defined {
return "Packet($!name) - " ~ $!link.Str;
} else {
return "Packet($!name) -|";
}
}
}
run_richards();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment