diff --git a/src/library_pthread.js b/src/library_pthread.js index 8e538098db58d..2a2f9174d8c9a 100644 --- a/src/library_pthread.js +++ b/src/library_pthread.js @@ -283,12 +283,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 @@ -329,9 +325,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 diff --git a/src/preamble.js b/src/preamble.js index 284b70163cb75..320652ad9a117 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -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 diff --git a/src/worker.js b/src/worker.js index 2ea9474f001ed..4a589fe17fda6 100644 --- a/src/worker.js +++ b/src/worker.js @@ -139,6 +139,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 diff --git a/test/test_other.py b/test/test_other.py index 7b45c1a0f2f07..ed31852514ac1 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -5598,6 +5598,37 @@ def test_require_modularize(self): self.assertFalse(output.stderr) self.assertEqual(output.stdout, 'hello, world!\nhello, world!\n') + @node_pthreads + def test_pthread_print_override_modularize(self): + self.set_setting('EXPORT_NAME', 'Test') + self.set_setting('PROXY_TO_PTHREAD') + self.set_setting('EXIT_RUNTIME') + self.set_setting('MODULARIZE') + create_file('main.c', ''' + #include + + int main() { + _emscripten_out("hello, world!"); + return 0; + } + ''') + 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('main.c', output_filename='test.js') + output = self.run_js('main.js') + self.assertNotContained('hello, world!', output) + self.assertContained('hello, earth!', output) + def test_define_modularize(self): self.run_process([EMCC, test_file('hello_world.c'), '-sMODULARIZE', '-sASSERTIONS=0']) src = 'var module = 0; ' + read_file('a.out.js')