Skip to content

Commit

Permalink
Proxy known handlers back to the main thread
Browse files Browse the repository at this point in the history
When running on a pthread, none of the incoming parameters on the
module object are present. Proxy known handlers back to the main
thread if specified.

This simplifies the previous `onAbort` pthread logic for a more
generic solution that also supports the `onExit`, `print` and
`printErr` handlers that can be overridden on the incoming module.

See: #17688.
  • Loading branch information
kleisauke committed Sep 28, 2022
1 parent fc3a217 commit 4c5f84d
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 20 deletions.
32 changes: 26 additions & 6 deletions src/library_pthread.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,12 +281,8 @@ var LibraryPThread = {
// Worker wants to postMessage() to itself to implement setImmediate()
// emulation.
worker.postMessage(d);
#if expectToReceiveOnModule('onAbort')
} else if (cmd === 'onAbort') {
if (Module['onAbort']) {
Module['onAbort'](d['arg']);
}
#endif
} else if (cmd === 'callHandler') {
Module[d['handler']](...d['args']);
} else if (cmd) {
// The received message looks like something that should be handled by this message
// handler, (since there is a e.data.cmd field present), but is not one of the
Expand Down Expand Up @@ -327,9 +323,33 @@ var LibraryPThread = {
assert(wasmModule instanceof WebAssembly.Module, 'WebAssembly Module should have been loaded by now!');
#endif

// When running on a pthread, none of the incoming parameters on the module
// object are present. Proxy known handlers back to the main thread if specified.
var handlers = [];
var knownHandlers = [
#if expectToReceiveOnModule('onExit')
'onExit',
#endif
#if expectToReceiveOnModule('onAbort')
'onAbort',
#endif
#if expectToReceiveOnModule('print')
'print',
#endif
#if expectToReceiveOnModule('printErr')
'printErr',
#endif
];
for (var handler of knownHandlers) {
if (Module.hasOwnProperty(handler)) {
handlers.push(handler);
}
}

// Ask the new worker to load up the Emscripten-compiled page. This is a heavy operation.
worker.postMessage({
'cmd': 'load',
'handlers': handlers,
// If the application main .js file was loaded from a Blob, then it is not possible
// to access the URL of the current script that could be passed to a Web Worker so that
// it could load up the same file. In that case, developer must either deliver the Blob
Expand Down
16 changes: 2 additions & 14 deletions src/preamble.js
Original file line number Diff line number Diff line change
Expand Up @@ -455,20 +455,8 @@ function removeRunDependency(id) {
/** @param {string|number=} what */
function abort(what) {
#if expectToReceiveOnModule('onAbort')
#if USE_PTHREADS
// When running on a pthread, none of the incoming parameters on the module
// object are present. The `onAbort` handler only exists on the main thread
// and so we need to proxy the handling of these back to the main thread.
// TODO(sbc): Extend this to all such handlers that can be passed into on
// module creation.
if (ENVIRONMENT_IS_PTHREAD) {
postMessage({ 'cmd': 'onAbort', 'arg': what});
} else
#endif
{
if (Module['onAbort']) {
Module['onAbort'](what);
}
if (Module['onAbort']) {
Module['onAbort'](what);
}
#endif
Expand Down
8 changes: 8 additions & 0 deletions src/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ self.onmessage = (e) => {
Module['dynamicLibraries'] = e.data.dynamicLibraries;
#endif

// Use `const` here to ensure that the variable is scoped only to
// that iteration, allowing safe reference from a closure.
for (const handler of e.data.handlers) {
Module[handler] = function() {
postMessage({ cmd: 'callHandler', handler, args: [...arguments] });
}
}

{{{ makeAsmImportsAccessInPthread('wasmMemory') }}} = e.data.wasmMemory;

#if LOAD_SOURCE_MAP
Expand Down
22 changes: 22 additions & 0 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -12038,6 +12038,28 @@ def test_wasmfs_jsfile_proxying_backend(self):
self.set_setting('EXIT_RUNTIME')
self.test_wasmfs_jsfile()

@node_pthreads
def test_wasmfs_print_override_pthread(self):
create_file('main.js', '''
const Test = require('./test.js');
async function main() {
await Test({
// world -> earth
print: (text) => console.log(text.replace('world', 'earth'))
});
}
main();
''')

self.emcc(test_file('hello_world.c'),
['-O3', '-sMODULARIZE', '-sEXPORT_NAME=Test', '-sWASMFS',
'-sEXIT_RUNTIME', '-pthread', '-sPROXY_TO_PTHREAD'],
output_filename='test.js')
output = self.run_js('main.js')
self.assertNotContained('hello, world!', output)
self.assertContained('hello, earth!', output)

def test_wasmfs_before_preload(self):
self.set_setting('WASMFS')
os.mkdir('js_backend_files')
Expand Down

0 comments on commit 4c5f84d

Please sign in to comment.