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