Skip to content

Commit 1c9080b

Browse files
authored
Merge branch 'main' into embind-shared-assertion
2 parents 15f69f2 + a0a3f24 commit 1c9080b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+499
-325
lines changed

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,12 @@ along with its entire output.
1919
Even for runtime issues it helps a lot if you can include the full link command.
2020
Adding `-v` to the link command will show all of the sub-commands run which
2121
can help us diagnose your issue.
22+
23+
Note: Where possible, please avoid attaching screen shots of code or console
24+
logs. Instead, please include them as text so that they may be copied /
25+
searched. To make code blocks more readable and syntax highlighted, please
26+
escape them with three backticks before and after, like this:
27+
28+
```cpp
29+
int x = 20;
30+
```

ChangeLog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ See docs/process.md for more on how version tagging works.
2424
developers and helps take a care of post-checkout tasks such as `npm install`.
2525
If this script needs to be run (e.g. becuase package.json was changed, emcc
2626
will exit with an error. (#19736)
27+
- If exceptions are disabled, using `new` together with `std::nothrow` no
28+
longer aborts if the allocation fails. Instead `nullptr` is returned now.
29+
This does not change the behavior of regular usage of `new`.
2730

2831
3.1.47 - 10/09/23
2932
-----------------

src/library.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,7 +1250,9 @@ addToLibrary({
12501250
// errno.h
12511251
// ==========================================================================
12521252

1253-
$ERRNO_CODES__postset: `ERRNO_CODES = {
1253+
// We use a string literal here to avoid the string quotes on the object
1254+
// keys being removed when processed by jsifier.
1255+
$ERRNO_CODES: `{
12541256
'EPERM': {{{ cDefs.EPERM }}},
12551257
'ENOENT': {{{ cDefs.ENOENT }}},
12561258
'ESRCH': {{{ cDefs.ESRCH }}},
@@ -1372,8 +1374,7 @@ addToLibrary({
13721374
'ENOTRECOVERABLE': {{{ cDefs.ENOTRECOVERABLE }}},
13731375
'EOWNERDEAD': {{{ cDefs.EOWNERDEAD }}},
13741376
'ESTRPIPE': {{{ cDefs.ESTRPIPE }}},
1375-
};`,
1376-
$ERRNO_CODES: {},
1377+
}`,
13771378
$ERRNO_MESSAGES: {
13781379
0: 'Success',
13791380
{{{ cDefs.EPERM }}}: 'Not super-user',

src/library_atomic.js

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/**
2+
* @license
3+
* Copyright 2023 The Emscripten Authors
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
7+
assert(SHARED_MEMORY);
8+
9+
addToLibrary({
10+
// Chrome 87 (and hence Edge 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_EDGE_VERSION < 91 || 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 || (typeof navigator !== 'undefined' && navigator.userAgent && jstoi_q((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+
$polyfillWaitAsync__deps: ['$jstoi_q'],
64+
#endif
65+
66+
$polyfillWaitAsync__internal: true,
67+
$polyfillWaitAsync: () => {
68+
// nop, used for its postset to ensure `Atomics.waitAsync()` polyfill is
69+
// included exactly once and only included when needed.
70+
// Any function using Atomics.waitAsync should depend on this.
71+
},
72+
73+
$atomicWaitStates__internal: true,
74+
$atomicWaitStates: ['ok', 'not-equal', 'timed-out'],
75+
$liveAtomicWaitAsyncs: {},
76+
$liveAtomicWaitAsyncs__internal: true,
77+
$liveAtomicWaitAsyncCounter: 0,
78+
$liveAtomicWaitAsyncCounter__internal: true,
79+
80+
emscripten_atomic_wait_async__deps: ['$atomicWaitStates', '$liveAtomicWaitAsyncs', '$liveAtomicWaitAsyncCounter', '$polyfillWaitAsync', '$callUserCallback'],
81+
emscripten_atomic_wait_async: (addr, val, asyncWaitFinished, userData, maxWaitMilliseconds) => {
82+
let wait = Atomics.waitAsync(HEAP32, {{{ getHeapOffset('addr', 'i32') }}}, val, maxWaitMilliseconds);
83+
if (!wait.async) return atomicWaitStates.indexOf(wait.value);
84+
// Increment waitAsync generation counter, account for wraparound in case
85+
// application does huge amounts of waitAsyncs per second (not sure if
86+
// possible?)
87+
// Valid counterrange: 0...2^31-1
88+
let counter = liveAtomicWaitAsyncCounter;
89+
liveAtomicWaitAsyncCounter = Math.max(0, (liveAtomicWaitAsyncCounter+1)|0);
90+
liveAtomicWaitAsyncs[counter] = addr;
91+
{{{ runtimeKeepalivePush() }}}
92+
wait.value.then((value) => {
93+
if (liveAtomicWaitAsyncs[counter]) {
94+
{{{ runtimeKeepalivePop() }}}
95+
delete liveAtomicWaitAsyncs[counter];
96+
callUserCallback(() => {{{ makeDynCall('vpiip', 'asyncWaitFinished') }}}(addr, val, atomicWaitStates.indexOf(value), userData));
97+
}
98+
});
99+
return -counter;
100+
},
101+
102+
emscripten_atomic_cancel_wait_async__deps: ['$liveAtomicWaitAsyncs'],
103+
emscripten_atomic_cancel_wait_async: (waitToken) => {
104+
#if ASSERTIONS
105+
if (waitToken == {{{ cDefs.ATOMICS_WAIT_NOT_EQUAL }}}) {
106+
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()');
107+
} else if (waitToken == {{{ cDefs.ATOMICS_WAIT_TIMED_OUT }}}) {
108+
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()');
109+
} else if (waitToken > 0) {
110+
warnOnce(`Attempted to call emscripten_atomic_cancel_wait_async() with an invalid wait token value ${waitToken}`);
111+
}
112+
#endif
113+
var address = liveAtomicWaitAsyncs[waitToken];
114+
if (address) {
115+
// Notify the waitAsync waiters on the memory location, so that JavaScript
116+
// garbage collection can occur.
117+
// See https://github.com/WebAssembly/threads/issues/176
118+
// This has the unfortunate effect of causing spurious wakeup of all other
119+
// waiters at the address (which causes a small performance loss).
120+
Atomics.notify(HEAP32, {{{ getHeapOffset('address', 'i32') }}});
121+
delete liveAtomicWaitAsyncs[waitToken];
122+
{{{ runtimeKeepalivePop() }}}
123+
return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}};
124+
}
125+
// This waitToken does not exist.
126+
return {{{ cDefs.EMSCRIPTEN_RESULT_INVALID_PARAM }}};
127+
},
128+
129+
emscripten_atomic_cancel_all_wait_asyncs__deps: ['$liveAtomicWaitAsyncs'],
130+
emscripten_atomic_cancel_all_wait_asyncs: () => {
131+
let waitAsyncs = Object.values(liveAtomicWaitAsyncs);
132+
waitAsyncs.forEach((address) => {
133+
Atomics.notify(HEAP32, {{{ getHeapOffset('address', 'i32') }}});
134+
});
135+
liveAtomicWaitAsyncs = {};
136+
return waitAsyncs.length;
137+
},
138+
139+
emscripten_atomic_cancel_all_wait_asyncs_at_address__deps: ['$liveAtomicWaitAsyncs'],
140+
emscripten_atomic_cancel_all_wait_asyncs_at_address: (address) => {
141+
let numCancelled = 0;
142+
Object.keys(liveAtomicWaitAsyncs).forEach((waitToken) => {
143+
if (liveAtomicWaitAsyncs[waitToken] == address) {
144+
Atomics.notify(HEAP32, {{{ getHeapOffset('address', 'i32') }}});
145+
delete liveAtomicWaitAsyncs[waitToken];
146+
numCancelled++;
147+
}
148+
});
149+
return numCancelled;
150+
},
151+
});

src/library_wasm_worker.js

Lines changed: 6 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
/**
2+
* @license
3+
* Copyright 2023 The Emscripten Authors
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
17
{{{
28
global.captureModuleArg = () => MODULARIZE ? '' : 'self.Module=d;';
39
global.instantiateModule = () => MODULARIZE ? `${EXPORT_NAME}(d);` : '';
@@ -241,137 +247,6 @@ if (ENVIRONMENT_IS_WASM_WORKER) {
241247
_wasmWorkers[id].postMessage({'_wsc': funcPtr, 'x': readEmAsmArgs(sigPtr, varargs) });
242248
},
243249

244-
$atomicWaitStates: "['ok', 'not-equal', 'timed-out']",
245-
246-
// Chrome 87 (and hence Edge 87) shipped Atomics.waitAsync (https://www.chromestatus.com/feature/6243382101803008)
247-
// However its implementation is faulty: https://bugs.chromium.org/p/chromium/issues/detail?id=1167541
248-
// Firefox Nightly 86.0a1 (2021-01-15) does not yet have it, https://bugzilla.mozilla.org/show_bug.cgi?id=1467846
249-
// And at the time of writing, no other browser has it either.
250-
#if MIN_EDGE_VERSION < 91 || MIN_CHROME_VERSION < 91 || MIN_SAFARI_VERSION != TARGET_NOT_SUPPORTED || MIN_FIREFOX_VERSION != TARGET_NOT_SUPPORTED || ENVIRONMENT_MAY_BE_NODE
251-
// Partially polyfill Atomics.waitAsync() if not available in the browser.
252-
// Also polyfill for old Chrome-based browsers, where Atomics.waitAsync is
253-
// broken until Chrome 91, see
254-
// https://bugs.chromium.org/p/chromium/issues/detail?id=1167541
255-
// https://github.com/tc39/proposal-atomics-wait-async/blob/master/PROPOSAL.md
256-
// This polyfill performs polling with setTimeout() to observe a change in the
257-
// target memory location.
258-
$polyfillWaitAsync__deps: ['$jstoi_q'],
259-
$polyfillWaitAsync__postset: `if (!Atomics.waitAsync || (typeof navigator !== 'undefined' && navigator.userAgent && jstoi_q((navigator.userAgent.match(/Chrom(e|ium)\\/([0-9]+)\\./)||[])[2]) < 91)) {
260-
let __Atomics_waitAsyncAddresses = [/*[i32a, index, value, maxWaitMilliseconds, promiseResolve]*/];
261-
function __Atomics_pollWaitAsyncAddresses() {
262-
let now = performance.now();
263-
let l = __Atomics_waitAsyncAddresses.length;
264-
for (let i = 0; i < l; ++i) {
265-
let a = __Atomics_waitAsyncAddresses[i];
266-
let expired = (now > a[3]);
267-
let awoken = (Atomics.load(a[0], a[1]) != a[2]);
268-
if (expired || awoken) {
269-
__Atomics_waitAsyncAddresses[i--] = __Atomics_waitAsyncAddresses[--l];
270-
__Atomics_waitAsyncAddresses.length = l;
271-
a[4](awoken ? 'ok': 'timed-out');
272-
}
273-
}
274-
if (l) {
275-
// If we still have addresses to wait, loop the timeout handler to continue polling.
276-
setTimeout(__Atomics_pollWaitAsyncAddresses, 10);
277-
}
278-
}
279-
#if ASSERTIONS
280-
if (!ENVIRONMENT_IS_WASM_WORKER) err('Current environment does not support Atomics.waitAsync(): polyfilling it, but this is going to be suboptimal.');
281-
#endif
282-
Atomics.waitAsync = (i32a, index, value, maxWaitMilliseconds) => {
283-
let val = Atomics.load(i32a, index);
284-
if (val != value) return { async: false, value: 'not-equal' };
285-
if (maxWaitMilliseconds <= 0) return { async: false, value: 'timed-out' };
286-
maxWaitMilliseconds = performance.now() + (maxWaitMilliseconds || Infinity);
287-
let promiseResolve;
288-
let promise = new Promise((resolve) => { promiseResolve = resolve; });
289-
if (!__Atomics_waitAsyncAddresses[0]) setTimeout(__Atomics_pollWaitAsyncAddresses, 10);
290-
__Atomics_waitAsyncAddresses.push([i32a, index, value, maxWaitMilliseconds, promiseResolve]);
291-
return { async: true, value: promise };
292-
};
293-
}`,
294-
#endif
295-
296-
$polyfillWaitAsync__internal: true,
297-
$polyfillWaitAsync: () => {
298-
// nop, used for its postset to ensure `Atomics.waitAsync()` polyfill is
299-
// included exactly once and only included when needed.
300-
// Any function using Atomics.waitAsync should depend on this.
301-
},
302-
303-
$liveAtomicWaitAsyncs: {},
304-
$liveAtomicWaitAsyncCounter: 0,
305-
306-
emscripten_atomic_wait_async__deps: ['$atomicWaitStates', '$liveAtomicWaitAsyncs', '$liveAtomicWaitAsyncCounter', '$polyfillWaitAsync'],
307-
emscripten_atomic_wait_async: (addr, val, asyncWaitFinished, userData, maxWaitMilliseconds) => {
308-
let wait = Atomics.waitAsync(HEAP32, {{{ getHeapOffset('addr', 'i32') }}}, val, maxWaitMilliseconds);
309-
if (!wait.async) return atomicWaitStates.indexOf(wait.value);
310-
// Increment waitAsync generation counter, account for wraparound in case
311-
// application does huge amounts of waitAsyncs per second (not sure if
312-
// possible?)
313-
// Valid counterrange: 0...2^31-1
314-
let counter = liveAtomicWaitAsyncCounter;
315-
liveAtomicWaitAsyncCounter = Math.max(0, (liveAtomicWaitAsyncCounter+1)|0);
316-
liveAtomicWaitAsyncs[counter] = addr;
317-
wait.value.then((value) => {
318-
if (liveAtomicWaitAsyncs[counter]) {
319-
delete liveAtomicWaitAsyncs[counter];
320-
{{{ makeDynCall('vpiip', 'asyncWaitFinished') }}}(addr, val, atomicWaitStates.indexOf(value), userData);
321-
}
322-
});
323-
return -counter;
324-
},
325-
326-
emscripten_atomic_cancel_wait_async__deps: ['$liveAtomicWaitAsyncs'],
327-
emscripten_atomic_cancel_wait_async: (waitToken) => {
328-
#if ASSERTIONS
329-
if (waitToken == {{{ cDefs.ATOMICS_WAIT_NOT_EQUAL }}}) {
330-
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()');
331-
} else if (waitToken == {{{ cDefs.ATOMICS_WAIT_TIMED_OUT }}}) {
332-
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()');
333-
} else if (waitToken > 0) {
334-
warnOnce(`Attempted to call emscripten_atomic_cancel_wait_async() with an invalid wait token value ${waitToken}`);
335-
}
336-
#endif
337-
var address = liveAtomicWaitAsyncs[waitToken];
338-
if (address) {
339-
// Notify the waitAsync waiters on the memory location, so that JavaScript
340-
// garbage collection can occur.
341-
// See https://github.com/WebAssembly/threads/issues/176
342-
// This has the unfortunate effect of causing spurious wakeup of all other
343-
// waiters at the address (which causes a small performance loss).
344-
Atomics.notify(HEAP32, {{{ getHeapOffset('address', 'i32') }}});
345-
delete liveAtomicWaitAsyncs[waitToken];
346-
return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}};
347-
}
348-
// This waitToken does not exist.
349-
return {{{ cDefs.EMSCRIPTEN_RESULT_INVALID_PARAM }}};
350-
},
351-
352-
emscripten_atomic_cancel_all_wait_asyncs__deps: ['$liveAtomicWaitAsyncs'],
353-
emscripten_atomic_cancel_all_wait_asyncs: () => {
354-
let waitAsyncs = Object.values(liveAtomicWaitAsyncs);
355-
waitAsyncs.forEach((address) => {
356-
Atomics.notify(HEAP32, {{{ getHeapOffset('address', 'i32') }}});
357-
});
358-
liveAtomicWaitAsyncs = {};
359-
return waitAsyncs.length;
360-
},
361-
362-
emscripten_atomic_cancel_all_wait_asyncs_at_address__deps: ['$liveAtomicWaitAsyncs'],
363-
emscripten_atomic_cancel_all_wait_asyncs_at_address: (address) => {
364-
let numCancelled = 0;
365-
Object.keys(liveAtomicWaitAsyncs).forEach((waitToken) => {
366-
if (liveAtomicWaitAsyncs[waitToken] == address) {
367-
Atomics.notify(HEAP32, {{{ getHeapOffset('address', 'i32') }}});
368-
delete liveAtomicWaitAsyncs[waitToken];
369-
numCancelled++;
370-
}
371-
});
372-
return numCancelled;
373-
},
374-
375250
emscripten_navigator_hardware_concurrency: () => {
376251
#if ENVIRONMENT_MAY_BE_NODE
377252
if (ENVIRONMENT_IS_NODE) return require('os').cpus().length;

src/modules.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ global.LibraryManager = {
153153
libraries.push('library_lz4.js');
154154
}
155155

156+
if (SHARED_MEMORY) {
157+
libraries.push('library_atomic.js');
158+
}
159+
156160
if (MAX_WEBGL_VERSION >= 2) {
157161
// library_webgl2.js must be included only after library_webgl.js, so if we are
158162
// about to include library_webgl2.js, first squeeze in library_webgl.js.

src/settings.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ var MALLOC = "dlmalloc";
140140
// which it did not previously. If you don't want that, just stop passing
141141
// it in at link time.
142142
//
143+
// Note that this setting does not affect the behavior of operator new in C++.
144+
// This function will always abort on allocation failure if exceptions are disabled.
145+
// If you want new to return 0 on failure, use it with std::nothrow.
146+
//
143147
// [link]
144148
var ABORTING_MALLOC = true;
145149

0 commit comments

Comments
 (0)