futex.js 7.38 KB
Newer Older
binji's avatar
binji committed
1 2 3 4
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
// Flags: --allow-natives-syntax --harmony-sharedarraybuffer
binji's avatar
binji committed
6

7
(function TestNonSharedArrayBehavior() {
binji's avatar
binji committed
8 9 10 11 12 13 14 15 16 17 18 19 20
  var ab = new ArrayBuffer(16);

  var i8a = new Int8Array(ab);
  var i16a = new Int16Array(ab);
  var i32a = new Int32Array(ab);
  var ui8a = new Uint8Array(ab);
  var ui8ca = new Uint8ClampedArray(ab);
  var ui16a = new Uint16Array(ab);
  var ui32a = new Uint32Array(ab);
  var f32a = new Float32Array(ab);
  var f64a = new Float64Array(ab);

  [i8a, i16a, i32a, ui8a, ui8ca, ui16a, ui32a, f32a, f64a].forEach(function(
21
    ta) {
22
    assertThrows(function() { Atomics.wait(ta, 0, 0); });
23 24 25 26 27
    if (ta === i32a) {
      assertEquals(0, Atomics.notify(ta, 0, 1));
    } else {
      assertThrows(function() { Atomics.notify(ta, 0, 1); });
    }
binji's avatar
binji committed
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
  });
})();

(function TestFailsWithNonSharedInt32Array() {
  var sab = new SharedArrayBuffer(16);

  var i8a = new Int8Array(sab);
  var i16a = new Int16Array(sab);
  var ui8a = new Uint8Array(sab);
  var ui8ca = new Uint8ClampedArray(sab);
  var ui16a = new Uint16Array(sab);
  var ui32a = new Uint32Array(sab);
  var f32a = new Float32Array(sab);
  var f64a = new Float64Array(sab);

  [i8a, i16a, ui8a, ui8ca, ui16a, ui32a, f32a, f64a].forEach(function(
      ta) {
45
    assertThrows(function() { Atomics.wait(ta, 0, 0); });
46
    assertThrows(function() { Atomics.notify(ta, 0, 1); });
binji's avatar
binji committed
47 48 49 50
  });
})();

(function TestInvalidIndex() {
51 52
  var sab = new SharedArrayBuffer(16);
  var i32a = new Int32Array(sab);
binji's avatar
binji committed
53 54

  // Valid indexes are 0-3.
55
  [-1, 4, 100, 0xffffffff].forEach(function(invalidIndex) {
56
    assertThrows(function() {
57
      Atomics.wait(i32a, invalidIndex, 0);
58 59
    }, RangeError);
    assertThrows(function() {
60
      Atomics.notify(i32a, invalidIndex, 0);
61
    }, RangeError);
binji's avatar
binji committed
62 63 64
    var validIndex = 0;
  });

65
  i32a = new Int32Array(sab, 8);
66
  [-1, 2, 100, 0xffffffff].forEach(function(invalidIndex) {
67
    assertThrows(function() {
68
      Atomics.wait(i32a, invalidIndex, 0);
69 70
    }, RangeError);
    assertThrows(function() {
71
      Atomics.notify(i32a, invalidIndex, 0);
72
    }, RangeError);
73 74
    var validIndex = 0;
  });
binji's avatar
binji committed
75 76 77 78 79 80
})();

(function TestWaitTimeout() {
  var i32a = new Int32Array(new SharedArrayBuffer(16));
  var waitMs = 100;
  var startTime = new Date();
81
  assertEquals("timed-out", Atomics.wait(i32a, 0, 0, waitMs));
binji's avatar
binji committed
82 83 84 85 86
  var endTime = new Date();
  assertTrue(endTime - startTime >= waitMs);
})();

(function TestWaitNotEqual() {
87 88
  var sab = new SharedArrayBuffer(16);
  var i32a = new Int32Array(sab);
89
  assertEquals("not-equal", Atomics.wait(i32a, 0, 42));
90 91 92

  i32a = new Int32Array(sab, 8);
  i32a[0] = 1;
93
  assertEquals("not-equal", Atomics.wait(i32a, 0, 0));
binji's avatar
binji committed
94 95 96 97
})();

(function TestWaitNegativeTimeout() {
  var i32a = new Int32Array(new SharedArrayBuffer(16));
98 99
  assertEquals("timed-out", Atomics.wait(i32a, 0, 0, -1));
  assertEquals("timed-out", Atomics.wait(i32a, 0, 0, -Infinity));
binji's avatar
binji committed
100 101
})();

102 103 104 105 106 107 108 109 110
(function TestWaitNotAllowed() {
  %SetAllowAtomicsWait(false);
  var i32a = new Int32Array(new SharedArrayBuffer(16));
  assertThrows(function() {
    Atomics.wait(i32a, 0, 0, -1);
  });
  %SetAllowAtomicsWait(true);
})();

111 112
(function TestWakePositiveInfinity() {
  var i32a = new Int32Array(new SharedArrayBuffer(16));
113
  Atomics.notify(i32a, 0, Number.POSITIVE_INFINITY);
114 115
})();

116 117 118 119 120 121 122 123
// In a previous version, this test caused a check failure
(function TestObjectWaitValue() {
  var sab = new SharedArrayBuffer(16);
  var i32a = new Int32Array(sab);
  assertEquals("timed-out", Atomics.wait(i32a, 0, Math, 0));
})();


binji's avatar
binji committed
124 125 126 127
//// WORKER ONLY TESTS

if (this.Worker) {

128
  var TestWaitWithTimeout = function(notify, timeout) {
binji's avatar
binji committed
129 130 131 132
    var sab = new SharedArrayBuffer(16);
    var i32a = new Int32Array(sab);

    var workerScript =
133 134
      `onmessage = function(msg) {
         var i32a = new Int32Array(msg.sab, msg.offset);
135
         var result = Atomics.wait(i32a, 0, 0, ${timeout});
binji's avatar
binji committed
136 137 138
         postMessage(result);
       };`;

139
    var worker = new Worker(workerScript, {type: 'string'});
140
    worker.postMessage({sab: sab, offset: offset});
binji's avatar
binji committed
141 142

    // Spin until the worker is waiting on the futex.
143
    while (%AtomicsNumWaitersForTesting(i32a, 0) != 1) {}
binji's avatar
binji committed
144

145
    notify(i32a, 0, 1);
146
    assertEquals("ok", worker.getMessage());
binji's avatar
binji committed
147
    worker.terminate();
148

149
    var worker2 = new Worker(workerScript, {type: 'string'});
150 151
    var offset = 8;
    var i32a2 = new Int32Array(sab, offset);
152
    worker2.postMessage({sab: sab, offset: offset});
153 154

    // Spin until the worker is waiting on the futex.
155
    while (%AtomicsNumWaitersForTesting(i32a2, 0) != 1) {}
156
    notify(i32a2, 0, 1);
157
    assertEquals("ok", worker2.getMessage());
158 159 160 161
    worker2.terminate();

    // Futex should work when index and buffer views are different, but
    // the real address is the same.
162
    var worker3 = new Worker(workerScript, {type: 'string'});
163
    i32a2 = new Int32Array(sab, 4);
164
    worker3.postMessage({sab: sab, offset: 8});
165 166

    // Spin until the worker is waiting on the futex.
167
    while (%AtomicsNumWaitersForTesting(i32a2, 1) != 1) {}
168
    notify(i32a2, 1, 1);
169
    assertEquals("ok", worker3.getMessage());
170
    worker3.terminate();
binji's avatar
binji committed
171 172 173
  };

  // Test various infinite timeouts
174 175 176 177 178
  TestWaitWithTimeout(Atomics.notify, undefined);
  TestWaitWithTimeout(Atomics.notify, NaN);
  TestWaitWithTimeout(Atomics.notify, Infinity);

  var TestWakeMulti = function(notify) {
binji's avatar
binji committed
179 180 181 182 183 184 185 186 187 188 189 190 191
    var sab = new SharedArrayBuffer(20);
    var i32a = new Int32Array(sab);

    // SAB values:
    // i32a[id], where id in range [0, 3]:
    //   0 => Worker |id| is still waiting on the futex
    //   1 => Worker |id| is not waiting on futex, but has not be reaped by the
    //        main thread.
    //   2 => Worker |id| has been reaped.
    //
    // i32a[4]:
    //   always 0. Each worker is waiting on this index.

192 193 194 195 196 197 198 199 200 201 202 203 204
    function workerCode() {
      onmessage = function(msg) {
        var id = msg.id;
        var i32a = new Int32Array(msg.sab);

        // Wait on i32a[4] (should be zero).
        var result = Atomics.wait(i32a, 4, 0);
        // Set i32a[id] to 1 to notify the main thread which workers were
        // woken up.
        Atomics.store(i32a, id, 1);
        postMessage(result);
      };
    }
binji's avatar
binji committed
205 206 207 208

    var id;
    var workers = [];
    for (id = 0; id < 4; id++) {
209
      workers[id] = new Worker(workerCode, {type: 'function'});
210
      workers[id].postMessage({sab: sab, id: id});
binji's avatar
binji committed
211 212 213
    }

    // Spin until all workers are waiting on the futex.
214
    while (%AtomicsNumWaitersForTesting(i32a, 4) != 4) {}
binji's avatar
binji committed
215 216

    // Wake up three waiters.
217
    assertEquals(3, notify(i32a, 4, 3));
binji's avatar
binji committed
218 219 220 221 222 223 224 225

    var wokenCount = 0;
    var waitingId = 0 + 1 + 2 + 3;
    while (wokenCount < 3) {
      for (id = 0; id < 4; id++) {
        // Look for workers that have not yet been reaped. Set i32a[id] to 2
        // when they've been processed so we don't look at them again.
        if (Atomics.compareExchange(i32a, id, 1, 2) == 1) {
226
          assertEquals("ok", workers[id].getMessage());
binji's avatar
binji committed
227 228 229 230 231 232 233 234 235
          workers[id].terminate();
          waitingId -= id;
          wokenCount++;
        }
      }
    }

    assertEquals(3, wokenCount);
    assertEquals(0, Atomics.load(i32a, waitingId));
236
    assertEquals(1, %AtomicsNumWaitersForTesting(i32a, 4));
binji's avatar
binji committed
237 238

    // Finally wake the last waiter.
239
    assertEquals(1, notify(i32a, 4, 1));
240
    assertEquals("ok", workers[waitingId].getMessage());
binji's avatar
binji committed
241 242
    workers[waitingId].terminate();

243
    assertEquals(0, %AtomicsNumWaitersForTesting(i32a, 4));
binji's avatar
binji committed
244

245
  };
binji's avatar
binji committed
246

247
  TestWakeMulti(Atomics.notify);
binji's avatar
binji committed
248
}