• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
102if (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