-
Notifications
You must be signed in to change notification settings - Fork 3.4k
/
Copy pathlibatomic.js
161 lines (150 loc) · 7.41 KB
/
libatomic.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/**
* @license
* Copyright 2023 The Emscripten Authors
* SPDX-License-Identifier: MIT
*/
assert(SHARED_MEMORY);
addToLibrary({
// Chrome 87 shipped Atomics.waitAsync:
// https://www.chromestatus.com/feature/6243382101803008
// However its implementation is faulty:
// https://bugs.chromium.org/p/chromium/issues/detail?id=1167541
// Firefox Nightly 86.0a1 (2021-01-15) does not yet have it:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1467846
// And at the time of writing, no other browser has it either.
#if MIN_CHROME_VERSION < 91 || MIN_SAFARI_VERSION != TARGET_NOT_SUPPORTED || MIN_FIREFOX_VERSION != TARGET_NOT_SUPPORTED || ENVIRONMENT_MAY_BE_NODE
// Partially polyfill Atomics.waitAsync() if not available in the browser.
// Also polyfill for old Chrome-based browsers, where Atomics.waitAsync is
// broken until Chrome 91, see:
// https://bugs.chromium.org/p/chromium/issues/detail?id=1167541
// https://github.com/tc39/proposal-atomics-wait-async/blob/master/PROPOSAL.md
// This polyfill performs polling with setTimeout() to observe a change in the
// target memory location.
$polyfillWaitAsync__postset: `if (!Atomics.waitAsync || (typeof navigator != 'undefined' && navigator.userAgent && jstoi_q((navigator.userAgent.match(/Chrom(e|ium)\\/([0-9]+)\\./)||[])[2]) < 91)) {
let __Atomics_waitAsyncAddresses = [/*[i32a, index, value, maxWaitMilliseconds, promiseResolve]*/];
function __Atomics_pollWaitAsyncAddresses() {
let now = performance.now();
let l = __Atomics_waitAsyncAddresses.length;
for (let i = 0; i < l; ++i) {
let a = __Atomics_waitAsyncAddresses[i];
let expired = (now > a[3]);
let awoken = (Atomics.load(a[0], a[1]) != a[2]);
if (expired || awoken) {
__Atomics_waitAsyncAddresses[i--] = __Atomics_waitAsyncAddresses[--l];
__Atomics_waitAsyncAddresses.length = l;
a[4](awoken ? 'ok': 'timed-out');
}
}
if (l) {
// If we still have addresses to wait, loop the timeout handler to continue polling.
setTimeout(__Atomics_pollWaitAsyncAddresses, 10);
}
}
#if ASSERTIONS && WASM_WORKERS
if (!ENVIRONMENT_IS_WASM_WORKER) err('Current environment does not support Atomics.waitAsync(): polyfilling it, but this is going to be suboptimal.');
#endif
/**
* @param {number=} maxWaitMilliseconds
*/
Atomics.waitAsync = (i32a, index, value, maxWaitMilliseconds) => {
let val = Atomics.load(i32a, index);
if (val != value) return { async: false, value: 'not-equal' };
if (maxWaitMilliseconds <= 0) return { async: false, value: 'timed-out' };
maxWaitMilliseconds = performance.now() + (maxWaitMilliseconds || Infinity);
let promiseResolve;
let promise = new Promise((resolve) => { promiseResolve = resolve; });
if (!__Atomics_waitAsyncAddresses[0]) setTimeout(__Atomics_pollWaitAsyncAddresses, 10);
__Atomics_waitAsyncAddresses.push([i32a, index, value, maxWaitMilliseconds, promiseResolve]);
return { async: true, value: promise };
};
}`,
$polyfillWaitAsync__deps: ['$jstoi_q'],
#endif
$polyfillWaitAsync__internal: true,
$polyfillWaitAsync: () => {
// nop, used for its postset to ensure `Atomics.waitAsync()` polyfill is
// included exactly once and only included when needed.
// Any function using Atomics.waitAsync should depend on this.
},
$atomicWaitStates__internal: true,
$atomicWaitStates: ['ok', 'not-equal', 'timed-out'],
$liveAtomicWaitAsyncs: {},
$liveAtomicWaitAsyncs__internal: true,
$liveAtomicWaitAsyncCounter: 0,
$liveAtomicWaitAsyncCounter__internal: true,
emscripten_atomic_wait_async__deps: ['$atomicWaitStates', '$liveAtomicWaitAsyncs', '$liveAtomicWaitAsyncCounter', '$polyfillWaitAsync', '$callUserCallback'],
emscripten_atomic_wait_async: (addr, val, asyncWaitFinished, userData, maxWaitMilliseconds) => {
let wait = Atomics.waitAsync(HEAP32, {{{ getHeapOffset('addr', 'i32') }}}, val, maxWaitMilliseconds);
if (!wait.async) return atomicWaitStates.indexOf(wait.value);
// Increment waitAsync generation counter, account for wraparound in case
// application does huge amounts of waitAsyncs per second (not sure if
// possible?)
// Valid counterrange: 0...2^31-1
let counter = liveAtomicWaitAsyncCounter;
liveAtomicWaitAsyncCounter = Math.max(0, (liveAtomicWaitAsyncCounter+1)|0);
liveAtomicWaitAsyncs[counter] = addr;
{{{ runtimeKeepalivePush() }}}
wait.value.then((value) => {
if (liveAtomicWaitAsyncs[counter]) {
{{{ runtimeKeepalivePop() }}}
delete liveAtomicWaitAsyncs[counter];
callUserCallback(() => {{{ makeDynCall('vpiip', 'asyncWaitFinished') }}}(addr, val, atomicWaitStates.indexOf(value), userData));
}
});
return -counter;
},
emscripten_atomic_cancel_wait_async__deps: ['$liveAtomicWaitAsyncs'],
emscripten_atomic_cancel_wait_async: (waitToken) => {
#if ASSERTIONS
if (waitToken == {{{ cDefs.ATOMICS_WAIT_NOT_EQUAL }}}) {
warnOnce('Attempted to call emscripten_atomic_cancel_wait_async() with a value ATOMICS_WAIT_NOT_EQUAL (1) that is not a valid wait token! Check success in return value from call to emscripten_atomic_wait_async()');
} else if (waitToken == {{{ cDefs.ATOMICS_WAIT_TIMED_OUT }}}) {
warnOnce('Attempted to call emscripten_atomic_cancel_wait_async() with a value ATOMICS_WAIT_TIMED_OUT (2) that is not a valid wait token! Check success in return value from call to emscripten_atomic_wait_async()');
} else if (waitToken > 0) {
warnOnce(`Attempted to call emscripten_atomic_cancel_wait_async() with an invalid wait token value ${waitToken}`);
}
#endif
var address = liveAtomicWaitAsyncs[waitToken];
if (address) {
// Notify the waitAsync waiters on the memory location, so that JavaScript
// garbage collection can occur.
// See https://github.com/WebAssembly/threads/issues/176
// This has the unfortunate effect of causing spurious wakeup of all other
// waiters at the address (which causes a small performance loss).
Atomics.notify(HEAP32, {{{ getHeapOffset('address', 'i32') }}});
delete liveAtomicWaitAsyncs[waitToken];
{{{ runtimeKeepalivePop() }}}
return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}};
}
// This waitToken does not exist.
return {{{ cDefs.EMSCRIPTEN_RESULT_INVALID_PARAM }}};
},
emscripten_atomic_cancel_all_wait_asyncs__deps: ['$liveAtomicWaitAsyncs'],
emscripten_atomic_cancel_all_wait_asyncs: () => {
let waitAsyncs = Object.values(liveAtomicWaitAsyncs);
waitAsyncs.forEach((address) => {
Atomics.notify(HEAP32, {{{ getHeapOffset('address', 'i32') }}});
});
liveAtomicWaitAsyncs = {};
return waitAsyncs.length;
},
emscripten_atomic_cancel_all_wait_asyncs_at_address__deps: ['$liveAtomicWaitAsyncs'],
emscripten_atomic_cancel_all_wait_asyncs_at_address: (address) => {
let numCancelled = 0;
Object.keys(liveAtomicWaitAsyncs).forEach((waitToken) => {
if (liveAtomicWaitAsyncs[waitToken] == address) {
Atomics.notify(HEAP32, {{{ getHeapOffset('address', 'i32') }}});
delete liveAtomicWaitAsyncs[waitToken];
numCancelled++;
}
});
return numCancelled;
},
emscripten_atomics_is_lock_free: (width) => Atomics.isLockFree(width),
emscripten_has_threading_support: () => typeof SharedArrayBuffer != 'undefined',
emscripten_num_logical_cores: () =>
#if ENVIRONMENT_MAY_BE_NODE
ENVIRONMENT_IS_NODE ? require('os').cpus().length :
#endif
navigator['hardwareConcurrency'],
});