|
7 | 7 | assert(SHARED_MEMORY);
|
8 | 8 |
|
9 | 9 | addToLibrary({
|
| 10 | +// Chrome 87 shipped Atomics.waitAsync: |
| 11 | +// https://www.chromestatus.com/feature/6243382101803008 |
| 12 | +// However its implementation is faulty: |
| 13 | +// https://bugs.chromium.org/p/chromium/issues/detail?id=1167541 |
| 14 | +// Firefox Nightly 86.0a1 (2021-01-15) does not yet have it: |
| 15 | +// https://bugzilla.mozilla.org/show_bug.cgi?id=1467846 |
| 16 | +// And at the time of writing, no other browser has it either. |
| 17 | +#if MIN_CHROME_VERSION < 91 || MIN_SAFARI_VERSION != TARGET_NOT_SUPPORTED || MIN_FIREFOX_VERSION != TARGET_NOT_SUPPORTED || ENVIRONMENT_MAY_BE_NODE |
| 18 | + // Partially polyfill Atomics.waitAsync() if not available in the browser. |
| 19 | + // Also polyfill for old Chrome-based browsers, where Atomics.waitAsync is |
| 20 | + // broken until Chrome 91, see: |
| 21 | + // https://bugs.chromium.org/p/chromium/issues/detail?id=1167541 |
| 22 | + // https://github.com/tc39/proposal-atomics-wait-async/blob/master/PROPOSAL.md |
| 23 | + // This polyfill performs polling with setTimeout() to observe a change in the |
| 24 | + // target memory location. |
| 25 | + $polyfillWaitAsync__postset: `if (!Atomics.waitAsync || (globalThis.navigator?.userAgent && Number((navigator.userAgent.match(/Chrom(e|ium)\\/([0-9]+)\\./)||[])[2]) < 91)) { |
| 26 | + let __Atomics_waitAsyncAddresses = [/*[i32a, index, value, maxWaitMilliseconds, promiseResolve]*/]; |
| 27 | + function __Atomics_pollWaitAsyncAddresses() { |
| 28 | + let now = performance.now(); |
| 29 | + let l = __Atomics_waitAsyncAddresses.length; |
| 30 | + for (let i = 0; i < l; ++i) { |
| 31 | + let a = __Atomics_waitAsyncAddresses[i]; |
| 32 | + let expired = (now > a[3]); |
| 33 | + let awoken = (Atomics.load(a[0], a[1]) != a[2]); |
| 34 | + if (expired || awoken) { |
| 35 | + __Atomics_waitAsyncAddresses[i--] = __Atomics_waitAsyncAddresses[--l]; |
| 36 | + __Atomics_waitAsyncAddresses.length = l; |
| 37 | + a[4](awoken ? 'ok': 'timed-out'); |
| 38 | + } |
| 39 | + } |
| 40 | + if (l) { |
| 41 | + // If we still have addresses to wait, loop the timeout handler to continue polling. |
| 42 | + setTimeout(__Atomics_pollWaitAsyncAddresses, 10); |
| 43 | + } |
| 44 | + } |
| 45 | + #if ASSERTIONS && WASM_WORKERS |
| 46 | + if (!ENVIRONMENT_IS_WASM_WORKER) err('Current environment does not support Atomics.waitAsync(): polyfilling it, but this is going to be suboptimal.'); |
| 47 | + #endif |
| 48 | + /** |
| 49 | + * @param {number=} maxWaitMilliseconds |
| 50 | + */ |
| 51 | + Atomics.waitAsync = (i32a, index, value, maxWaitMilliseconds) => { |
| 52 | + let val = Atomics.load(i32a, index); |
| 53 | + if (val != value) return { async: false, value: 'not-equal' }; |
| 54 | + if (maxWaitMilliseconds <= 0) return { async: false, value: 'timed-out' }; |
| 55 | + maxWaitMilliseconds = performance.now() + (maxWaitMilliseconds || Infinity); |
| 56 | + let promiseResolve; |
| 57 | + let promise = new Promise((resolve) => { promiseResolve = resolve; }); |
| 58 | + if (!__Atomics_waitAsyncAddresses[0]) setTimeout(__Atomics_pollWaitAsyncAddresses, 10); |
| 59 | + __Atomics_waitAsyncAddresses.push([i32a, index, value, maxWaitMilliseconds, promiseResolve]); |
| 60 | + return { async: true, value: promise }; |
| 61 | + }; |
| 62 | +}`, |
| 63 | +#endif |
| 64 | + |
| 65 | + $polyfillWaitAsync__internal: true, |
| 66 | + $polyfillWaitAsync: () => { |
| 67 | + // nop, used for its postset to ensure `Atomics.waitAsync()` polyfill is |
| 68 | + // included exactly once and only included when needed. |
| 69 | + // Any function using Atomics.waitAsync should depend on this. |
| 70 | + }, |
| 71 | + |
10 | 72 | $atomicWaitStates__internal: true,
|
11 | 73 | $atomicWaitStates: ['ok', 'not-equal', 'timed-out'],
|
12 | 74 | $liveAtomicWaitAsyncs: {},
|
13 | 75 | $liveAtomicWaitAsyncs__internal: true,
|
14 | 76 | $liveAtomicWaitAsyncCounter: 0,
|
15 | 77 | $liveAtomicWaitAsyncCounter__internal: true,
|
16 | 78 |
|
17 |
| - emscripten_atomic_wait_async__deps: ['$atomicWaitStates', '$liveAtomicWaitAsyncs', '$liveAtomicWaitAsyncCounter', '$callUserCallback'], |
| 79 | + emscripten_atomic_wait_async__deps: ['$atomicWaitStates', '$liveAtomicWaitAsyncs', '$liveAtomicWaitAsyncCounter', '$polyfillWaitAsync', '$callUserCallback'], |
18 | 80 | emscripten_atomic_wait_async: (addr, val, asyncWaitFinished, userData, maxWaitMilliseconds) => {
|
19 | 81 | let wait = Atomics.waitAsync(HEAP32, {{{ getHeapOffset('addr', 'i32') }}}, val, maxWaitMilliseconds);
|
20 | 82 | if (!wait.async) return atomicWaitStates.indexOf(wait.value);
|
|
0 commit comments