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