1 // Copyright 2006-2008 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 // This is a JavaScript implementation of the Richards
32 // http://www.cl.cam.ac.uk/~mr10/Bench.html
34 // The benchmark was originally implemented in BCPL by
38 let __exceptionCounter
= 0;
39 function randomException() {
41 if (__exceptionCounter
% 5000 === 0) {
42 throw new Error("rando");
45 noInline(randomException
);
48 * The Richards benchmark simulates the task dispatcher of an
51 function runRichards() {
53 var scheduler
= new Scheduler();
54 scheduler
.addIdleTask(ID_IDLE
, 0, null, COUNT
);
56 var queue
= new Packet(null, ID_WORKER
, KIND_WORK
);
57 queue
= new Packet(queue
, ID_WORKER
, KIND_WORK
);
58 scheduler
.addWorkerTask(ID_WORKER
, 1000, queue
);
60 queue
= new Packet(null, ID_DEVICE_A
, KIND_DEVICE
);
61 queue
= new Packet(queue
, ID_DEVICE_A
, KIND_DEVICE
);
62 queue
= new Packet(queue
, ID_DEVICE_A
, KIND_DEVICE
);
63 scheduler
.addHandlerTask(ID_HANDLER_A
, 2000, queue
);
65 queue
= new Packet(null, ID_DEVICE_B
, KIND_DEVICE
);
66 queue
= new Packet(queue
, ID_DEVICE_B
, KIND_DEVICE
);
67 queue
= new Packet(queue
, ID_DEVICE_B
, KIND_DEVICE
);
68 scheduler
.addHandlerTask(ID_HANDLER_B
, 3000, queue
);
70 scheduler
.addDeviceTask(ID_DEVICE_A
, 4000, null);
72 scheduler
.addDeviceTask(ID_DEVICE_B
, 5000, null);
76 if (scheduler
.queueCount
!= EXPECTED_QUEUE_COUNT
||
77 scheduler
.holdCount
!= EXPECTED_HOLD_COUNT
) {
79 "Error during execution: queueCount = " + scheduler
.queueCount
+
80 ", holdCount = " + scheduler
.holdCount
+ ".";
91 * These two constants specify how many times a packet is queued and
92 * how many times a task is put on hold in a correct run of richards.
93 * They don't have any meaning a such but are characteristic of a
94 * correct run so if the actual queue or hold count is different from
95 * the expected there must be a bug in the implementation.
97 var EXPECTED_QUEUE_COUNT
= 2322;
98 var EXPECTED_HOLD_COUNT
= 928;
102 * A scheduler can be used to schedule a set of tasks based on their relative
103 * priorities. Scheduling is done by maintaining a list of task control blocks
104 * which holds tasks and the data queue they are processing.
107 function Scheduler() {
111 this.blocks
= new Array(NUMBER_OF_IDS
);
113 this.currentTcb
= null;
114 this.currentId
= null;
122 var ID_HANDLER_A
= 2;
123 var ID_HANDLER_B
= 3;
126 var NUMBER_OF_IDS
= 6;
132 * Add an idle task to this scheduler.
133 * @param {int} id the identity of the task
134 * @param {int} priority the task's priority
135 * @param {Packet} queue the queue of work to be processed by the task
136 * @param {int} count the number of times to schedule the task
138 Scheduler
.prototype.addIdleTask = function (id
, priority
, queue
, count
) {
140 this.addRunningTask(id
, priority
, queue
, new IdleTask(this, 1, count
));
146 * Add a work task to this scheduler.
147 * @param {int} id the identity of the task
148 * @param {int} priority the task's priority
149 * @param {Packet} queue the queue of work to be processed by the task
151 Scheduler
.prototype.addWorkerTask = function (id
, priority
, queue
) {
153 this.addTask(id
, priority
, queue
, new WorkerTask(this, ID_HANDLER_A
, 0));
159 * Add a handler task to this scheduler.
160 * @param {int} id the identity of the task
161 * @param {int} priority the task's priority
162 * @param {Packet} queue the queue of work to be processed by the task
164 Scheduler
.prototype.addHandlerTask = function (id
, priority
, queue
) {
166 this.addTask(id
, priority
, queue
, new HandlerTask(this));
172 * Add a handler task to this scheduler.
173 * @param {int} id the identity of the task
174 * @param {int} priority the task's priority
175 * @param {Packet} queue the queue of work to be processed by the task
177 Scheduler
.prototype.addDeviceTask = function (id
, priority
, queue
) {
179 this.addTask(id
, priority
, queue
, new DeviceTask(this))
185 * Add the specified task and mark it as running.
186 * @param {int} id the identity of the task
187 * @param {int} priority the task's priority
188 * @param {Packet} queue the queue of work to be processed by the task
189 * @param {Task} task the task to add
191 Scheduler
.prototype.addRunningTask = function (id
, priority
, queue
, task
) {
193 this.addTask(id
, priority
, queue
, task
);
194 this.currentTcb
.setRunning();
200 * Add the specified task to this scheduler.
201 * @param {int} id the identity of the task
202 * @param {int} priority the task's priority
203 * @param {Packet} queue the queue of work to be processed by the task
204 * @param {Task} task the task to add
206 Scheduler
.prototype.addTask = function (id
, priority
, queue
, task
) {
208 this.currentTcb
= new TaskControlBlock(this.list
, id
, priority
, queue
, task
);
209 this.list
= this.currentTcb
;
210 this.blocks
[id
] = this.currentTcb
;
216 * Execute the tasks managed by this scheduler.
218 Scheduler
.prototype.schedule = function () {
219 this.currentTcb
= this.list
;
220 while (this.currentTcb
!= null) {
222 if (this.currentTcb
.isHeldOrSuspended()) {
223 this.currentTcb
= this.currentTcb
.link
;
225 this.currentId
= this.currentTcb
.id
;
226 this.currentTcb
= this.currentTcb
.run();
234 * Release a task that is currently blocked and return the next block to run.
235 * @param {int} id the id of the task to suspend
237 Scheduler
.prototype.release = function (id
) {
239 var tcb
= this.blocks
[id
];
240 if (tcb
== null) return tcb
;
245 if (tcb
.priority
> this.currentTcb
.priority
) {
248 return this.currentTcb
;
254 * Block the currently executing task and return the next task control block
255 * to run. The blocked task will not be made runnable until it is explicitly
256 * released, even if new work is added to it.
258 Scheduler
.prototype.holdCurrent = function () {
261 this.currentTcb
.markAsHeld();
264 return this.currentTcb
.link
;
268 * Suspend the currently executing task and return the next task control block
269 * to run. If new work is added to the suspended task it will be made runnable.
271 Scheduler
.prototype.suspendCurrent = function () {
273 this.currentTcb
.markAsSuspended();
277 return this.currentTcb
;
282 * Add the specified packet to the end of the worklist used by the task
283 * associated with the packet and make the task runnable if it is currently
285 * @param {Packet} packet the packet to add
287 Scheduler
.prototype.queue = function (packet
) {
289 var t
= this.blocks
[packet
.id
];
290 if (t
== null) return t
;
295 packet
.id
= this.currentId
;
296 return t
.checkPriorityAdd(this.currentTcb
, packet
);
300 * A task control block manages a task and the queue of work packages associated
302 * @param {TaskControlBlock} link the preceding block in the linked block list
303 * @param {int} id the id of this block
304 * @param {int} priority the priority of this block
305 * @param {Packet} queue the queue of packages to be processed by the task
306 * @param {Task} task the task
309 function TaskControlBlock(link
, id
, priority
, queue
, task
) {
313 this.priority
= priority
;
320 this.state
= STATE_SUSPENDED
;
322 this.state
= STATE_SUSPENDED_RUNNABLE
;
329 * The task is running and is currently scheduled.
331 var STATE_RUNNING
= 0;
334 * The task has packets left to process.
336 var STATE_RUNNABLE
= 1;
339 * The task is not currently running. The task is not blocked as such and may
340 * be started by the scheduler.
342 var STATE_SUSPENDED
= 2;
345 * The task is blocked and cannot be run until it is explicitly released.
349 var STATE_SUSPENDED_RUNNABLE
= STATE_SUSPENDED
| STATE_RUNNABLE
;
350 var STATE_NOT_HELD
= ~STATE_HELD
;
352 TaskControlBlock
.prototype.setRunning = function () {
354 this.state
= STATE_RUNNING
;
359 TaskControlBlock
.prototype.markAsNotHeld = function () {
361 this.state
= this.state
& STATE_NOT_HELD
;
366 TaskControlBlock
.prototype.markAsHeld = function () {
368 this.state
= this.state
| STATE_HELD
;
373 TaskControlBlock
.prototype.isHeldOrSuspended = function () {
376 return (this.state
& STATE_HELD
) != 0 || (this.state
== STATE_SUSPENDED
);
378 return (this.state
& STATE_HELD
) != 0 || (this.state
== STATE_SUSPENDED
);
382 TaskControlBlock
.prototype.markAsSuspended = function () {
385 this.state
= this.state
| STATE_SUSPENDED
;
387 this.state
= this.state
| STATE_SUSPENDED
;
391 TaskControlBlock
.prototype.markAsRunnable = function () {
394 this.state
= this.state
| STATE_RUNNABLE
;
396 this.state
= this.state
| STATE_RUNNABLE
;
401 * Runs this task, if it is ready to be run, and returns the next task to run.
403 TaskControlBlock
.prototype.run = function () {
406 if (this.state
== STATE_SUSPENDED_RUNNABLE
) {
408 this.queue
= packet
.link
;
409 if (this.queue
== null) {
410 this.state
= STATE_RUNNING
;
412 this.state
= STATE_RUNNABLE
;
419 return this.task
.run(packet
);
423 * Adds a packet to the worklist of this block's task, marks this as runnable if
424 * necessary, and returns the next runnable object to run (the one
425 * with the highest priority).
427 TaskControlBlock
.prototype.checkPriorityAdd = function (task
, packet
) {
429 if (this.queue
== null) {
431 this.markAsRunnable();
432 if (this.priority
> task
.priority
) return this;
434 this.queue
= packet
.addTo(this.queue
);
444 TaskControlBlock
.prototype.toString = function () {
447 return "tcb { " + this.task
+ "@" + this.state
+ " }";
449 return "tcb { " + this.task
+ "@" + this.state
+ " }";
454 * An idle task doesn't do any work itself but cycles control between the two
456 * @param {Scheduler} scheduler the scheduler that manages this task
457 * @param {int} v1 a seed value that controls how the device tasks are scheduled
458 * @param {int} count the number of times this task should be scheduled
461 function IdleTask(scheduler
, v1
, count
) {
463 this.scheduler
= scheduler
;
472 IdleTask
.prototype.run = function (packet
) {
475 if (this.count
== 0) return this.scheduler
.holdCurrent();
480 if ((this.v1
& 1) == 0) {
481 this.v1
= this.v1
>> 1;
482 return this.scheduler
.release(ID_DEVICE_A
);
484 this.v1
= (this.v1
>> 1) ^ 0xD008;
485 return this.scheduler
.release(ID_DEVICE_B
);
490 IdleTask
.prototype.toString = function () {
500 * A task that suspends itself after each time it has been run to simulate
501 * waiting for data from an external device.
502 * @param {Scheduler} scheduler the scheduler that manages this task
505 function DeviceTask(scheduler
) {
507 this.scheduler
= scheduler
;
513 DeviceTask
.prototype.run = function (packet
) {
514 if (packet
== null) {
516 if (this.v1
== null) return this.scheduler
.suspendCurrent();
521 return this.scheduler
.queue(v
);
527 return this.scheduler
.holdCurrent();
531 DeviceTask
.prototype.toString = function () {
539 * A task that manipulates work packets.
540 * @param {Scheduler} scheduler the scheduler that manages this task
541 * @param {int} v1 a seed used to specify how work packets are manipulated
542 * @param {int} v2 another seed used to specify how work packets are manipulated
545 function WorkerTask(scheduler
, v1
, v2
) {
547 this.scheduler
= scheduler
;
554 WorkerTask
.prototype.run = function (packet
) {
555 if (packet
== null) {
556 return this.scheduler
.suspendCurrent();
559 if (this.v1
== ID_HANDLER_A
) {
560 this.v1
= ID_HANDLER_B
;
562 this.v1
= ID_HANDLER_A
;
568 for (var i
= 0; i
< DATA_SIZE
; i
++) {
571 if (this.v2
> 26) this.v2
= 1;
572 packet
.a2
[i
] = this.v2
;
576 return this.scheduler
.queue(packet
);
580 WorkerTask
.prototype.toString = function () {
587 * A task that manipulates work packets and then suspends itself.
588 * @param {Scheduler} scheduler the scheduler that manages this task
591 function HandlerTask(scheduler
) {
593 this.scheduler
= scheduler
;
600 HandlerTask
.prototype.run = function (packet
) {
602 if (packet
!= null) {
603 if (packet
.kind
== KIND_WORK
) {
604 this.v1
= packet
.addTo(this.v1
);
606 this.v2
= packet
.addTo(this.v2
);
613 if (this.v1
!= null) {
614 var count
= this.v1
.a1
;
616 if (count
< DATA_SIZE
) {
617 if (this.v2
!= null) {
619 this.v2
= this.v2
.link
;
620 v
.a1
= this.v1
.a2
[count
];
621 this.v1
.a1
= count
+ 1;
622 return this.scheduler
.queue(v
);
626 this.v1
= this.v1
.link
;
627 return this.scheduler
.queue(v
);
632 return this.scheduler
.suspendCurrent();
635 HandlerTask
.prototype.toString = function () {
637 return "HandlerTask";
648 * A simple package of data that is manipulated by the tasks. The exact layout
649 * of the payload data carried by a packet is not importaint, and neither is the
650 * nature of the work performed on packets by the tasks.
652 * Besides carrying data, packets form linked lists and are hence used both as
653 * data and worklists.
654 * @param {Packet} link the tail of the linked list of packets
655 * @param {int} id an ID for this packet
656 * @param {int} kind the type of this packet
659 function Packet(link
, id
, kind
) {
665 this.a2
= new Array(DATA_SIZE
);
671 * Add this packet to the end of a worklist, and return the worklist.
672 * @param {Packet} queue the worklist to add this packet to
674 Packet
.prototype.addTo = function (queue
) {
676 if (queue
== null) return this;
677 var peek
, next
= queue
;
678 while ((peek
= next
.link
) != null) {
688 Packet
.prototype.toString = function () {
694 for (let i
= 0; i
< 350; ++i
)