tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

check-richards.js (15631B)


      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
      4 // met:
      5 //
      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.
     15 //
     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.
     27 
     28 
     29 // This is a JavaScript implementation of the Richards
     30 // benchmark from:
     31 //
     32 //    http://www.cl.cam.ac.uk/~mr10/Bench.html
     33 //
     34 // The benchmark was originally implemented in BCPL by
     35 // Martin Richards.
     36 
     37 
     38 //var Richards = new BenchmarkSuite('Richards', 34886, [
     39 //  new Benchmark("Richards", runRichards)
     40 //]);
     41 
     42 
     43 /**
     44 * The Richards benchmark simulates the task dispatcher of an
     45 * operating system.
     46 **/
     47 function runRichards() {
     48  var scheduler = new Scheduler();
     49  scheduler.addIdleTask(ID_IDLE, 0, null, COUNT);
     50 
     51  var queue = new Packet(null, ID_WORKER, KIND_WORK);
     52  queue = new Packet(queue,  ID_WORKER, KIND_WORK);
     53  scheduler.addWorkerTask(ID_WORKER, 1000, queue);
     54 
     55  queue = new Packet(null, ID_DEVICE_A, KIND_DEVICE);
     56  queue = new Packet(queue,  ID_DEVICE_A, KIND_DEVICE);
     57  queue = new Packet(queue,  ID_DEVICE_A, KIND_DEVICE);
     58  scheduler.addHandlerTask(ID_HANDLER_A, 2000, queue);
     59 
     60  queue = new Packet(null, ID_DEVICE_B, KIND_DEVICE);
     61  queue = new Packet(queue,  ID_DEVICE_B, KIND_DEVICE);
     62  queue = new Packet(queue,  ID_DEVICE_B, KIND_DEVICE);
     63  scheduler.addHandlerTask(ID_HANDLER_B, 3000, queue);
     64 
     65  scheduler.addDeviceTask(ID_DEVICE_A, 4000, null);
     66 
     67  scheduler.addDeviceTask(ID_DEVICE_B, 5000, null);
     68 
     69  scheduler.schedule();
     70 
     71  assertEq(scheduler.queueCount, EXPECTED_QUEUE_COUNT);
     72  assertEq(scheduler.holdCount, EXPECTED_HOLD_COUNT);
     73 }
     74 
     75 var COUNT = 1000;
     76 
     77 /**
     78 * These two constants specify how many times a packet is queued and
     79 * how many times a task is put on hold in a correct run of richards.
     80 * They don't have any meaning a such but are characteristic of a
     81 * correct run so if the actual queue or hold count is different from
     82 * the expected there must be a bug in the implementation.
     83 **/
     84 var EXPECTED_QUEUE_COUNT = 2322;
     85 var EXPECTED_HOLD_COUNT = 928;
     86 
     87 
     88 /**
     89 * A scheduler can be used to schedule a set of tasks based on their relative
     90 * priorities.  Scheduling is done by maintaining a list of task control blocks
     91 * which holds tasks and the data queue they are processing.
     92 * @constructor
     93 */
     94 function Scheduler() {
     95  this.queueCount = 0;
     96  this.holdCount = 0;
     97  this.blocks = new Array(NUMBER_OF_IDS);
     98  this.list = null;
     99  this.currentTcb = null;
    100  this.currentId = null;
    101 }
    102 
    103 var ID_IDLE       = 0;
    104 var ID_WORKER     = 1;
    105 var ID_HANDLER_A  = 2;
    106 var ID_HANDLER_B  = 3;
    107 var ID_DEVICE_A   = 4;
    108 var ID_DEVICE_B   = 5;
    109 var NUMBER_OF_IDS = 6;
    110 
    111 var KIND_DEVICE   = 0;
    112 var KIND_WORK     = 1;
    113 
    114 /**
    115 * Add an idle task to this scheduler.
    116 * @param {int} id the identity of the task
    117 * @param {int} priority the task's priority
    118 * @param {Packet} queue the queue of work to be processed by the task
    119 * @param {int} count the number of times to schedule the task
    120 */
    121 Scheduler.prototype.addIdleTask = function (id, priority, queue, count) {
    122  this.addRunningTask(id, priority, queue, new IdleTask(this, 1, count));
    123 };
    124 
    125 /**
    126 * Add a work task to this scheduler.
    127 * @param {int} id the identity of the task
    128 * @param {int} priority the task's priority
    129 * @param {Packet} queue the queue of work to be processed by the task
    130 */
    131 Scheduler.prototype.addWorkerTask = function (id, priority, queue) {
    132  this.addTask(id, priority, queue, new WorkerTask(this, ID_HANDLER_A, 0));
    133 };
    134 
    135 /**
    136 * Add a handler task to this scheduler.
    137 * @param {int} id the identity of the task
    138 * @param {int} priority the task's priority
    139 * @param {Packet} queue the queue of work to be processed by the task
    140 */
    141 Scheduler.prototype.addHandlerTask = function (id, priority, queue) {
    142  this.addTask(id, priority, queue, new HandlerTask(this));
    143 };
    144 
    145 /**
    146 * Add a handler 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
    150 */
    151 Scheduler.prototype.addDeviceTask = function (id, priority, queue) {
    152  this.addTask(id, priority, queue, new DeviceTask(this))
    153 };
    154 
    155 /**
    156 * Add the specified task and mark it as running.
    157 * @param {int} id the identity of the task
    158 * @param {int} priority the task's priority
    159 * @param {Packet} queue the queue of work to be processed by the task
    160 * @param {Task} task the task to add
    161 */
    162 Scheduler.prototype.addRunningTask = function (id, priority, queue, task) {
    163  this.addTask(id, priority, queue, task);
    164  this.currentTcb.setRunning();
    165 };
    166 
    167 /**
    168 * Add the specified task to this scheduler.
    169 * @param {int} id the identity of the task
    170 * @param {int} priority the task's priority
    171 * @param {Packet} queue the queue of work to be processed by the task
    172 * @param {Task} task the task to add
    173 */
    174 Scheduler.prototype.addTask = function (id, priority, queue, task) {
    175  this.currentTcb = new TaskControlBlock(this.list, id, priority, queue, task);
    176  this.list = this.currentTcb;
    177  this.blocks[id] = this.currentTcb;
    178 };
    179 
    180 /**
    181 * Execute the tasks managed by this scheduler.
    182 */
    183 Scheduler.prototype.schedule = function () {
    184  this.currentTcb = this.list;
    185  while (this.currentTcb != null) {
    186    if (this.currentTcb.isHeldOrSuspended()) {
    187      this.currentTcb = this.currentTcb.link;
    188    } else {
    189      this.currentId = this.currentTcb.id;
    190      this.currentTcb = this.currentTcb.run();
    191    }
    192  }
    193 };
    194 
    195 /**
    196 * Release a task that is currently blocked and return the next block to run.
    197 * @param {int} id the id of the task to suspend
    198 */
    199 Scheduler.prototype.release = function (id) {
    200  var tcb = this.blocks[id];
    201  if (tcb == null) return tcb;
    202  tcb.markAsNotHeld();
    203  if (tcb.priority > this.currentTcb.priority) {
    204    return tcb;
    205  } else {
    206    return this.currentTcb;
    207  }
    208 };
    209 
    210 /**
    211 * Block the currently executing task and return the next task control block
    212 * to run.  The blocked task will not be made runnable until it is explicitly
    213 * released, even if new work is added to it.
    214 */
    215 Scheduler.prototype.holdCurrent = function () {
    216  this.holdCount++;
    217  this.currentTcb.markAsHeld();
    218  return this.currentTcb.link;
    219 };
    220 
    221 /**
    222 * Suspend the currently executing task and return the next task control block
    223 * to run.  If new work is added to the suspended task it will be made runnable.
    224 */
    225 Scheduler.prototype.suspendCurrent = function () {
    226  this.currentTcb.markAsSuspended();
    227  return this.currentTcb;
    228 };
    229 
    230 /**
    231 * Add the specified packet to the end of the worklist used by the task
    232 * associated with the packet and make the task runnable if it is currently
    233 * suspended.
    234 * @param {Packet} packet the packet to add
    235 */
    236 Scheduler.prototype.queue = function (packet) {
    237  var t = this.blocks[packet.id];
    238  if (t == null) return t;
    239  this.queueCount++;
    240  packet.link = null;
    241  packet.id = this.currentId;
    242  return t.checkPriorityAdd(this.currentTcb, packet);
    243 };
    244 
    245 /**
    246 * A task control block manages a task and the queue of work packages associated
    247 * with it.
    248 * @param {TaskControlBlock} link the preceding block in the linked block list
    249 * @param {int} id the id of this block
    250 * @param {int} priority the priority of this block
    251 * @param {Packet} queue the queue of packages to be processed by the task
    252 * @param {Task} task the task
    253 * @constructor
    254 */
    255 function TaskControlBlock(link, id, priority, queue, task) {
    256  this.link = link;
    257  this.id = id;
    258  this.priority = priority;
    259  this.queue = queue;
    260  this.task = task;
    261  if (queue == null) {
    262    this.state = STATE_SUSPENDED;
    263  } else {
    264    this.state = STATE_SUSPENDED_RUNNABLE;
    265  }
    266 }
    267 
    268 /**
    269 * The task is running and is currently scheduled.
    270 */
    271 var STATE_RUNNING = 0;
    272 
    273 /**
    274 * The task has packets left to process.
    275 */
    276 var STATE_RUNNABLE = 1;
    277 
    278 /**
    279 * The task is not currently running.  The task is not blocked as such and may
    280 * be started by the scheduler.
    281 */
    282 var STATE_SUSPENDED = 2;
    283 
    284 /**
    285 * The task is blocked and cannot be run until it is explicitly released.
    286 */
    287 var STATE_HELD = 4;
    288 
    289 var STATE_SUSPENDED_RUNNABLE = STATE_SUSPENDED | STATE_RUNNABLE;
    290 var STATE_NOT_HELD = ~STATE_HELD;
    291 
    292 TaskControlBlock.prototype.setRunning = function () {
    293  this.state = STATE_RUNNING;
    294 };
    295 
    296 TaskControlBlock.prototype.markAsNotHeld = function () {
    297  this.state = this.state & STATE_NOT_HELD;
    298 };
    299 
    300 TaskControlBlock.prototype.markAsHeld = function () {
    301  this.state = this.state | STATE_HELD;
    302 };
    303 
    304 TaskControlBlock.prototype.isHeldOrSuspended = function () {
    305  return (this.state & STATE_HELD) != 0 || (this.state == STATE_SUSPENDED);
    306 };
    307 
    308 TaskControlBlock.prototype.markAsSuspended = function () {
    309  this.state = this.state | STATE_SUSPENDED;
    310 };
    311 
    312 TaskControlBlock.prototype.markAsRunnable = function () {
    313  this.state = this.state | STATE_RUNNABLE;
    314 };
    315 
    316 /**
    317 * Runs this task, if it is ready to be run, and returns the next task to run.
    318 */
    319 TaskControlBlock.prototype.run = function () {
    320  var packet;
    321  if (this.state == STATE_SUSPENDED_RUNNABLE) {
    322    packet = this.queue;
    323    this.queue = packet.link;
    324    if (this.queue == null) {
    325      this.state = STATE_RUNNING;
    326    } else {
    327      this.state = STATE_RUNNABLE;
    328    }
    329  } else {
    330    packet = null;
    331  }
    332  return this.task.run(packet);
    333 };
    334 
    335 /**
    336 * Adds a packet to the worklist of this block's task, marks this as runnable if
    337 * necessary, and returns the next runnable object to run (the one
    338 * with the highest priority).
    339 */
    340 TaskControlBlock.prototype.checkPriorityAdd = function (task, packet) {
    341  if (this.queue == null) {
    342    this.queue = packet;
    343    this.markAsRunnable();
    344    if (this.priority > task.priority) return this;
    345  } else {
    346    this.queue = packet.addTo(this.queue);
    347  }
    348  return task;
    349 };
    350 
    351 TaskControlBlock.prototype.toString = function () {
    352  return "tcb { " + this.task + "@" + this.state + " }";
    353 };
    354 
    355 /**
    356 * An idle task doesn't do any work itself but cycles control between the two
    357 * device tasks.
    358 * @param {Scheduler} scheduler the scheduler that manages this task
    359 * @param {int} v1 a seed value that controls how the device tasks are scheduled
    360 * @param {int} count the number of times this task should be scheduled
    361 * @constructor
    362 */
    363 function IdleTask(scheduler, v1, count) {
    364  this.scheduler = scheduler;
    365  this.v1 = v1;
    366  this.count = count;
    367 }
    368 
    369 IdleTask.prototype.run = function (packet) {
    370  this.count--;
    371  if (this.count == 0) return this.scheduler.holdCurrent();
    372  if ((this.v1 & 1) == 0) {
    373    this.v1 = this.v1 >> 1;
    374    return this.scheduler.release(ID_DEVICE_A);
    375  } else {
    376    this.v1 = (this.v1 >> 1) ^ 0xD008;
    377    return this.scheduler.release(ID_DEVICE_B);
    378  }
    379 };
    380 
    381 IdleTask.prototype.toString = function () {
    382  return "IdleTask"
    383 };
    384 
    385 /**
    386 * A task that suspends itself after each time it has been run to simulate
    387 * waiting for data from an external device.
    388 * @param {Scheduler} scheduler the scheduler that manages this task
    389 * @constructor
    390 */
    391 function DeviceTask(scheduler) {
    392  this.scheduler = scheduler;
    393  this.v1 = null;
    394 }
    395 
    396 DeviceTask.prototype.run = function (packet) {
    397  if (packet == null) {
    398    if (this.v1 == null) return this.scheduler.suspendCurrent();
    399    var v = this.v1;
    400    this.v1 = null;
    401    return this.scheduler.queue(v);
    402  } else {
    403    this.v1 = packet;
    404    return this.scheduler.holdCurrent();
    405  }
    406 };
    407 
    408 DeviceTask.prototype.toString = function () {
    409  return "DeviceTask";
    410 };
    411 
    412 /**
    413 * A task that manipulates work packets.
    414 * @param {Scheduler} scheduler the scheduler that manages this task
    415 * @param {int} v1 a seed used to specify how work packets are manipulated
    416 * @param {int} v2 another seed used to specify how work packets are manipulated
    417 * @constructor
    418 */
    419 function WorkerTask(scheduler, v1, v2) {
    420  this.scheduler = scheduler;
    421  this.v1 = v1;
    422  this.v2 = v2;
    423 }
    424 
    425 WorkerTask.prototype.run = function (packet) {
    426  if (packet == null) {
    427    return this.scheduler.suspendCurrent();
    428  } else {
    429    if (this.v1 == ID_HANDLER_A) {
    430      this.v1 = ID_HANDLER_B;
    431    } else {
    432      this.v1 = ID_HANDLER_A;
    433    }
    434    packet.id = this.v1;
    435    packet.a1 = 0;
    436    for (var i = 0; i < DATA_SIZE; i++) {
    437      this.v2++;
    438      if (this.v2 > 26) this.v2 = 1;
    439      packet.a2[i] = this.v2;
    440    }
    441    return this.scheduler.queue(packet);
    442  }
    443 };
    444 
    445 WorkerTask.prototype.toString = function () {
    446  return "WorkerTask";
    447 };
    448 
    449 /**
    450 * A task that manipulates work packets and then suspends itself.
    451 * @param {Scheduler} scheduler the scheduler that manages this task
    452 * @constructor
    453 */
    454 function HandlerTask(scheduler) {
    455  this.scheduler = scheduler;
    456  this.v1 = null;
    457  this.v2 = null;
    458 }
    459 
    460 HandlerTask.prototype.run = function (packet) {
    461  if (packet != null) {
    462    if (packet.kind == KIND_WORK) {
    463      this.v1 = packet.addTo(this.v1);
    464    } else {
    465      this.v2 = packet.addTo(this.v2);
    466    }
    467  }
    468  if (this.v1 != null) {
    469    var count = this.v1.a1;
    470    var v;
    471    if (count < DATA_SIZE) {
    472      if (this.v2 != null) {
    473        v = this.v2;
    474        this.v2 = this.v2.link;
    475        v.a1 = this.v1.a2[count];
    476        this.v1.a1 = count + 1;
    477        return this.scheduler.queue(v);
    478      }
    479    } else {
    480      v = this.v1;
    481      this.v1 = this.v1.link;
    482      return this.scheduler.queue(v);
    483    }
    484  }
    485  return this.scheduler.suspendCurrent();
    486 };
    487 
    488 HandlerTask.prototype.toString = function () {
    489  return "HandlerTask";
    490 };
    491 
    492 /* --- *
    493 * P a c k e t
    494 * --- */
    495 
    496 var DATA_SIZE = 4;
    497 
    498 /**
    499 * A simple package of data that is manipulated by the tasks.  The exact layout
    500 * of the payload data carried by a packet is not importaint, and neither is the
    501 * nature of the work performed on packets by the tasks.
    502 *
    503 * Besides carrying data, packets form linked lists and are hence used both as
    504 * data and worklists.
    505 * @param {Packet} link the tail of the linked list of packets
    506 * @param {int} id an ID for this packet
    507 * @param {int} kind the type of this packet
    508 * @constructor
    509 */
    510 function Packet(link, id, kind) {
    511  this.link = link;
    512  this.id = id;
    513  this.kind = kind;
    514  this.a1 = 0;
    515  this.a2 = new Array(DATA_SIZE);
    516 }
    517 
    518 /**
    519 * Add this packet to the end of a worklist, and return the worklist.
    520 * @param {Packet} queue the worklist to add this packet to
    521 */
    522 Packet.prototype.addTo = function (queue) {
    523  this.link = null;
    524  if (queue == null) return this;
    525  var peek, next = queue;
    526  while ((peek = next.link) != null)
    527    next = peek;
    528  next.link = this;
    529  return queue;
    530 };
    531 
    532 Packet.prototype.toString = function () {
    533  return "Packet";
    534 };
    535 
    536 runRichards();