Home | History | Annotate | Download | only in harmony
      1 // Copyright 2015 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Flags: --allow-natives-syntax --harmony-sharedarraybuffer
      6 
      7 (function TestFailsWithNonSharedArray() {
      8   var ab = new ArrayBuffer(16);
      9 
     10   var i8a = new Int8Array(ab);
     11   var i16a = new Int16Array(ab);
     12   var i32a = new Int32Array(ab);
     13   var ui8a = new Uint8Array(ab);
     14   var ui8ca = new Uint8ClampedArray(ab);
     15   var ui16a = new Uint16Array(ab);
     16   var ui32a = new Uint32Array(ab);
     17   var f32a = new Float32Array(ab);
     18   var f64a = new Float64Array(ab);
     19 
     20   [i8a, i16a, i32a, ui8a, ui8ca, ui16a, ui32a, f32a, f64a].forEach(function(
     21       ta) {
     22     assertThrows(function() { Atomics.futexWait(ta, 0, 0); });
     23     assertThrows(function() { Atomics.futexWake(ta, 0, 1); });
     24     assertThrows(function() { Atomics.futexWakeOrRequeue(ta, 0, 1, 0, 0); });
     25   });
     26 })();
     27 
     28 (function TestFailsWithNonSharedInt32Array() {
     29   var sab = new SharedArrayBuffer(16);
     30 
     31   var i8a = new Int8Array(sab);
     32   var i16a = new Int16Array(sab);
     33   var ui8a = new Uint8Array(sab);
     34   var ui8ca = new Uint8ClampedArray(sab);
     35   var ui16a = new Uint16Array(sab);
     36   var ui32a = new Uint32Array(sab);
     37   var f32a = new Float32Array(sab);
     38   var f64a = new Float64Array(sab);
     39 
     40   [i8a, i16a, ui8a, ui8ca, ui16a, ui32a, f32a, f64a].forEach(function(
     41       ta) {
     42     assertThrows(function() { Atomics.futexWait(ta, 0, 0); });
     43     assertThrows(function() { Atomics.futexWake(ta, 0, 1); });
     44     assertThrows(function() { Atomics.futexWakeOrRequeue(ta, 0, 1, 0, 0); });
     45   });
     46 })();
     47 
     48 (function TestInvalidIndex() {
     49   var sab = new SharedArrayBuffer(16);
     50   var i32a = new Int32Array(sab);
     51 
     52   // Valid indexes are 0-3.
     53   [-1, 4, 100].forEach(function(invalidIndex) {
     54     assertEquals(undefined, Atomics.futexWait(i32a, invalidIndex, 0));
     55     assertEquals(undefined, Atomics.futexWake(i32a, invalidIndex, 0));
     56     var validIndex = 0;
     57     assertEquals(undefined, Atomics.futexWakeOrRequeue(i32a, invalidIndex, 0, 0,
     58                                                        validIndex));
     59     assertEquals(undefined, Atomics.futexWakeOrRequeue(i32a, validIndex, 0, 0,
     60                                                        invalidIndex));
     61   });
     62 
     63   i32a = new Int32Array(sab, 8);
     64   [-1, 2, 100].forEach(function(invalidIndex) {
     65     assertEquals(undefined, Atomics.futexWait(i32a, invalidIndex, 0));
     66     assertEquals(undefined, Atomics.futexWake(i32a, invalidIndex, 0));
     67     var validIndex = 0;
     68     assertEquals(undefined, Atomics.futexWakeOrRequeue(i32a, invalidIndex, 0, 0,
     69                                                        validIndex));
     70     assertEquals(undefined, Atomics.futexWakeOrRequeue(i32a, validIndex, 0, 0,
     71                                                        invalidIndex));
     72   });
     73 })();
     74 
     75 (function TestWaitTimeout() {
     76   var i32a = new Int32Array(new SharedArrayBuffer(16));
     77   var waitMs = 100;
     78   var startTime = new Date();
     79   assertEquals(Atomics.TIMEDOUT, Atomics.futexWait(i32a, 0, 0, waitMs));
     80   var endTime = new Date();
     81   assertTrue(endTime - startTime >= waitMs);
     82 })();
     83 
     84 (function TestWaitNotEqual() {
     85   var sab = new SharedArrayBuffer(16);
     86   var i32a = new Int32Array(sab);
     87   assertEquals(Atomics.NOTEQUAL, Atomics.futexWait(i32a, 0, 42));
     88 
     89   i32a = new Int32Array(sab, 8);
     90   i32a[0] = 1;
     91   assertEquals(Atomics.NOTEQUAL, Atomics.futexWait(i32a, 0, 0));
     92 })();
     93 
     94 (function TestWaitNegativeTimeout() {
     95   var i32a = new Int32Array(new SharedArrayBuffer(16));
     96   assertEquals(Atomics.TIMEDOUT, Atomics.futexWait(i32a, 0, 0, -1));
     97   assertEquals(Atomics.TIMEDOUT, Atomics.futexWait(i32a, 0, 0, -Infinity));
     98 })();
     99 
    100 //// WORKER ONLY TESTS
    101 
    102 if (this.Worker) {
    103 
    104   var TestWaitWithTimeout = function(timeout) {
    105     var sab = new SharedArrayBuffer(16);
    106     var i32a = new Int32Array(sab);
    107 
    108     var workerScript =
    109       `onmessage = function(msg) {
    110          var i32a = new Int32Array(msg.sab, msg.offset);
    111          var result = Atomics.futexWait(i32a, 0, 0, ${timeout});
    112          postMessage(result);
    113        };`;
    114 
    115     var worker = new Worker(workerScript);
    116     worker.postMessage({sab: sab, offset: offset}, [sab]);
    117 
    118     // Spin until the worker is waiting on the futex.
    119     while (%AtomicsFutexNumWaitersForTesting(i32a, 0) != 1) {}
    120 
    121     Atomics.futexWake(i32a, 0, 1);
    122     assertEquals(Atomics.OK, worker.getMessage());
    123     worker.terminate();
    124 
    125     var worker2 = new Worker(workerScript);
    126     var offset = 8;
    127     var i32a2 = new Int32Array(sab, offset);
    128     worker2.postMessage({sab: sab, offset: offset}, [sab]);
    129 
    130     // Spin until the worker is waiting on the futex.
    131     while (%AtomicsFutexNumWaitersForTesting(i32a2, 0) != 1) {}
    132     Atomics.futexWake(i32a2, 0, 1);
    133     assertEquals(Atomics.OK, worker2.getMessage());
    134     worker2.terminate();
    135 
    136     // Futex should work when index and buffer views are different, but
    137     // the real address is the same.
    138     var worker3 = new Worker(workerScript);
    139     i32a2 = new Int32Array(sab, 4);
    140     worker3.postMessage({sab: sab, offset: 8}, [sab]);
    141 
    142     // Spin until the worker is waiting on the futex.
    143     while (%AtomicsFutexNumWaitersForTesting(i32a2, 1) != 1) {}
    144     Atomics.futexWake(i32a2, 1, 1);
    145     assertEquals(Atomics.OK, worker3.getMessage());
    146     worker3.terminate();
    147   };
    148 
    149   // Test various infinite timeouts
    150   TestWaitWithTimeout(undefined);
    151   TestWaitWithTimeout(NaN);
    152   TestWaitWithTimeout(Infinity);
    153 
    154 
    155   (function TestWakeMulti() {
    156     var sab = new SharedArrayBuffer(20);
    157     var i32a = new Int32Array(sab);
    158 
    159     // SAB values:
    160     // i32a[id], where id in range [0, 3]:
    161     //   0 => Worker |id| is still waiting on the futex
    162     //   1 => Worker |id| is not waiting on futex, but has not be reaped by the
    163     //        main thread.
    164     //   2 => Worker |id| has been reaped.
    165     //
    166     // i32a[4]:
    167     //   always 0. Each worker is waiting on this index.
    168 
    169     var workerScript =
    170       `onmessage = function(msg) {
    171          var id = msg.id;
    172          var i32a = new Int32Array(msg.sab);
    173 
    174          // Wait on i32a[4] (should be zero).
    175          var result = Atomics.futexWait(i32a, 4, 0);
    176          // Set i32a[id] to 1 to notify the main thread which workers were
    177          // woken up.
    178          Atomics.store(i32a, id, 1);
    179          postMessage(result);
    180        };`;
    181 
    182     var id;
    183     var workers = [];
    184     for (id = 0; id < 4; id++) {
    185       workers[id] = new Worker(workerScript);
    186       workers[id].postMessage({sab: sab, id: id}, [sab]);
    187     }
    188 
    189     // Spin until all workers are waiting on the futex.
    190     while (%AtomicsFutexNumWaitersForTesting(i32a, 4) != 4) {}
    191 
    192     // Wake up three waiters.
    193     assertEquals(3, Atomics.futexWake(i32a, 4, 3));
    194 
    195     var wokenCount = 0;
    196     var waitingId = 0 + 1 + 2 + 3;
    197     while (wokenCount < 3) {
    198       for (id = 0; id < 4; id++) {
    199         // Look for workers that have not yet been reaped. Set i32a[id] to 2
    200         // when they've been processed so we don't look at them again.
    201         if (Atomics.compareExchange(i32a, id, 1, 2) == 1) {
    202           assertEquals(Atomics.OK, workers[id].getMessage());
    203           workers[id].terminate();
    204           waitingId -= id;
    205           wokenCount++;
    206         }
    207       }
    208     }
    209 
    210     assertEquals(3, wokenCount);
    211     assertEquals(0, Atomics.load(i32a, waitingId));
    212     assertEquals(1, %AtomicsFutexNumWaitersForTesting(i32a, 4));
    213 
    214     // Finally wake the last waiter.
    215     assertEquals(1, Atomics.futexWake(i32a, 4, 1));
    216     assertEquals(Atomics.OK, workers[waitingId].getMessage());
    217     workers[waitingId].terminate();
    218 
    219     assertEquals(0, %AtomicsFutexNumWaitersForTesting(i32a, 4));
    220 
    221   })();
    222 
    223   (function TestWakeOrRequeue() {
    224     var sab = new SharedArrayBuffer(24);
    225     var i32a = new Int32Array(sab);
    226 
    227     // SAB values:
    228     // i32a[id], where id in range [0, 3]:
    229     //   0 => Worker |id| is still waiting on the futex
    230     //   1 => Worker |id| is not waiting on futex, but has not be reaped by the
    231     //        main thread.
    232     //   2 => Worker |id| has been reaped.
    233     //
    234     // i32a[4]:
    235     //   always 0. Each worker will initially wait on this index.
    236     //
    237     // i32a[5]:
    238     //   always 0. Requeued workers will wait on this index.
    239 
    240     var workerScript =
    241       `onmessage = function(msg) {
    242          var id = msg.id;
    243          var i32a = new Int32Array(msg.sab);
    244 
    245          var result = Atomics.futexWait(i32a, 4, 0, Infinity);
    246          Atomics.store(i32a, id, 1);
    247          postMessage(result);
    248        };`;
    249 
    250     var workers = [];
    251     for (id = 0; id < 4; id++) {
    252       workers[id] = new Worker(workerScript);
    253       workers[id].postMessage({sab: sab, id: id}, [sab]);
    254     }
    255 
    256     // Spin until all workers are waiting on the futex.
    257     while (%AtomicsFutexNumWaitersForTesting(i32a, 4) != 4) {}
    258 
    259     var index1 = 4;
    260     var index2 = 5;
    261 
    262     // If futexWakeOrRequeue is called with the incorrect value, it shouldn't
    263     // wake any waiters.
    264     assertEquals(Atomics.NOTEQUAL,
    265                  Atomics.futexWakeOrRequeue(i32a, index1, 1, 42, index2));
    266 
    267     assertEquals(4, %AtomicsFutexNumWaitersForTesting(i32a, index1));
    268     assertEquals(0, %AtomicsFutexNumWaitersForTesting(i32a, index2));
    269 
    270     // Now wake with the correct value.
    271     assertEquals(1, Atomics.futexWakeOrRequeue(i32a, index1, 1, 0, index2));
    272 
    273     // The workers that are still waiting should atomically be transferred to
    274     // the new index.
    275     assertEquals(3, %AtomicsFutexNumWaitersForTesting(i32a, index2));
    276 
    277     // The woken worker may not have been scheduled yet. Look for which thread
    278     // has set its i32a value to 1.
    279     var wokenCount = 0;
    280     while (wokenCount < 1) {
    281       for (id = 0; id < 4; id++) {
    282         if (Atomics.compareExchange(i32a, id, 1, 2) == 1) {
    283           wokenCount++;
    284         }
    285       }
    286     }
    287 
    288     assertEquals(0, %AtomicsFutexNumWaitersForTesting(i32a, index1));
    289 
    290     // Wake the remaining waiters.
    291     assertEquals(3, Atomics.futexWake(i32a, index2, 3));
    292 
    293     // As above, wait until the workers have been scheduled.
    294     wokenCount = 0;
    295     while (wokenCount < 3) {
    296       for (id = 0; id < 4; id++) {
    297         if (Atomics.compareExchange(i32a, id, 1, 2) == 1) {
    298           wokenCount++;
    299         }
    300       }
    301     }
    302 
    303     assertEquals(0, %AtomicsFutexNumWaitersForTesting(i32a, index1));
    304     assertEquals(0, %AtomicsFutexNumWaitersForTesting(i32a, index2));
    305 
    306     for (id = 0; id < 4; ++id) {
    307       assertEquals(Atomics.OK, workers[id].getMessage());
    308     }
    309 
    310     // Test futexWakeOrRequeue on offset typed array
    311     var offset = 16;
    312     sab = new SharedArrayBuffer(24);
    313     i32a = new Int32Array(sab);
    314     var i32a2 = new Int32Array(sab, offset);
    315 
    316     for (id = 0; id < 4; id++) {
    317       workers[id].postMessage({sab: sab, id: id}, [sab]);
    318     }
    319 
    320     while (%AtomicsFutexNumWaitersForTesting(i32a2, 0) != 4) { }
    321 
    322     index1 = 0;
    323     index2 = 1;
    324     assertEquals(4, %AtomicsFutexNumWaitersForTesting(i32a2, index1));
    325     assertEquals(0, %AtomicsFutexNumWaitersForTesting(i32a2, index2));
    326 
    327     assertEquals(2, Atomics.futexWakeOrRequeue(i32a2, index1, 2, 0, index2));
    328     assertEquals(2, %AtomicsFutexNumWaitersForTesting(i32a2, index2));
    329     assertEquals(0, %AtomicsFutexNumWaitersForTesting(i32a2, index1));
    330 
    331     assertEquals(2, Atomics.futexWake(i32a2, index2, 2));
    332 
    333     for (id = 0; id < 4; ++id) {
    334       assertEquals(Atomics.OK, workers[id].getMessage());
    335       workers[id].terminate();
    336     }
    337 
    338   })();
    339 
    340 }
    341