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