Skip to content

Commit

Permalink
Fix wasm workers under node
Browse files Browse the repository at this point in the history
  • Loading branch information
sbc100 committed Oct 11, 2024
1 parent 40e6b04 commit f53fb7a
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 27 deletions.
18 changes: 8 additions & 10 deletions src/library_wasm_worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,13 @@ addToLibrary({
},

// Executes a wasm function call received via a postMessage.
$_wasmWorkerRunPostMessage__deps: ['$callUserCallback'],
$_wasmWorkerRunPostMessage: (e) => {
// '_wsc' is short for 'wasm call', trying to use an identifier name that
// will never conflict with user code
#if ENVIRONMENT_MAY_BE_NODE
// In Node.js environment, message event 'e' containing the actual data sent,
// while in the browser environment it's contained by 'e.data'.
let data = ENVIRONMENT_IS_NODE ? e : e.data;
#else
let data = e.data;
#endif
let wasmCall = data['_wsc'];
wasmCall && getWasmTableEntry(wasmCall)(...data['x']);
wasmCall && callUserCallback(() => getWasmTableEntry(wasmCall)(...data['x']));
},

// src/postamble_minimal.js brings this symbol in to the build, and calls this
Expand Down Expand Up @@ -195,6 +190,11 @@ if (ENVIRONMENT_IS_WASM_WORKER
#endif
});
worker.onmessage = _wasmWorkerRunPostMessage;
#if ENVIRONMENT_MAY_BE_NODE
if (ENVIRONMENT_IS_NODE) {
worker.on('message', (msg) => worker.onmessage({ data: msg }));
}
#endif
return _wasmWorkersID++;
},

Expand All @@ -212,9 +212,7 @@ if (ENVIRONMENT_IS_WASM_WORKER
#if ASSERTIONS
assert(!ENVIRONMENT_IS_WASM_WORKER, 'emscripten_terminate_all_wasm_workers() cannot be called from a Wasm Worker: only the main browser thread has visibility to terminate all Workers!');
#endif
Object.values(_wasmWorkers).forEach((worker) => {
worker.terminate();
});
Object.values(_wasmWorkers).forEach((worker) => worker.terminate());
_wasmWorkers = {};
},

Expand Down
2 changes: 1 addition & 1 deletion src/runtime_pthread.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ if (ENVIRONMENT_IS_PTHREAD) {
// Create as web-worker-like an environment as we can.

var parentPort = worker_threads['parentPort'];
parentPort.on('message', (data) => onmessage({ data: data }));
parentPort.on('message', (msg) => onmessage({ data: msg }));

Object.assign(globalThis, {
self: global,
Expand Down
12 changes: 9 additions & 3 deletions src/wasm_worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ if (ENVIRONMENT_IS_NODE) {

var parentPort = nodeWorkerThreads.parentPort;

parentPort.on('message', (data) => typeof onmessage === "function" && onmessage({ data: data }));
parentPort.on('message', (msg) => global.onmessage?.({ data: msg }));

var wrappedHandlers = {}
function wrapMsgHandler(h) {
wrappedHandlers[h] ??= (msg) => h({data: msg});
return wrappedHandlers[h];
}

var fs = require('fs');
var vm = require('vm');
Expand All @@ -28,8 +34,8 @@ if (ENVIRONMENT_IS_NODE) {
importScripts: (f) => vm.runInThisContext(fs.readFileSync(f, 'utf8'), {filename: f}),
postMessage: (msg) => parentPort.postMessage(msg),
performance: global.performance || { now: Date.now },
addEventListener: (name, handler) => parentPort.on(name, handler),
removeEventListener: (name, handler) => parentPort.off(name, handler),
addEventListener: (name, handler) => parentPort.on(name, wrapMsgHandler(handler)),
removeEventListener: (name, handler) => parentPort.off(name, wrapMsgHandler(handler)),
});
}
#endif // ENVIRONMENT_MAY_BE_NODE
Expand Down
10 changes: 5 additions & 5 deletions test/test_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4985,21 +4985,21 @@ def test_system(self):
# Tests the hello_wasm_worker.c documentation example code.
@also_with_minimal_runtime
def test_wasm_worker_hello(self):
self.btest('wasm_worker/hello_wasm_worker.c', expected='0', args=['-sWASM_WORKERS'])
self.btest_exit('wasm_worker/hello_wasm_worker.c', args=['-sWASM_WORKERS'])

def test_wasm_worker_hello_minimal_runtime_2(self):
self.btest('wasm_worker/hello_wasm_worker.c', expected='0', args=['-sWASM_WORKERS', '-sMINIMAL_RUNTIME=2'])
self.btest_exit('wasm_worker/hello_wasm_worker.c', args=['-sWASM_WORKERS', '-sMINIMAL_RUNTIME=2'])

# Tests Wasm Workers build in Wasm2JS mode.
@requires_wasm2js
@also_with_minimal_runtime
def test_wasm_worker_hello_wasm2js(self):
self.btest('wasm_worker/hello_wasm_worker.c', expected='0', args=['-sWASM_WORKERS', '-sWASM=0'])
self.btest_exit('wasm_worker/hello_wasm_worker.c', args=['-sWASM_WORKERS', '-sWASM=0'])

# Tests the WASM_WORKERS=2 build mode, which embeds the Wasm Worker bootstrap JS script file to the main JS file.
@also_with_minimal_runtime
def test_wasm_worker_embedded(self):
self.btest('wasm_worker/hello_wasm_worker.c', expected='0', args=['-sWASM_WORKERS=2'])
def test_wasm_worker_hello_embedded(self):
self.btest_exit('wasm_worker/hello_wasm_worker.c', args=['-sWASM_WORKERS=2'])

# Tests that it is possible to call emscripten_futex_wait() in Wasm Workers.
@parameterized({
Expand Down
3 changes: 3 additions & 0 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -13889,6 +13889,9 @@ def test_debug_opt_warning(self, should_fail, args):
else:
self.run_process([EMCC, test_file('hello_world.c'), '-Werror'] + args)

def test_wasm_worker_hello(self):
self.do_runf(test_file('wasm_worker/hello_wasm_worker.c'), emcc_args=['-sWASM_WORKERS'])

@also_with_minimal_runtime
def test_wasm_worker_closure(self):
self.run_process([EMCC, test_file('wasm_worker/lock_async_acquire.c'), '-O2', '-sWASM_WORKERS', '--closure=1'])
Expand Down
18 changes: 10 additions & 8 deletions test/wasm_worker/hello_wasm_worker.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@
#include <assert.h>

// This is the code example in site/source/docs/api_reference/wasm_workers.rst
void do_exit() {
emscripten_out("do_exit");
emscripten_terminate_all_wasm_workers();
emscripten_force_exit(0);
}

void run_in_worker()
{
emscripten_console_log("Hello from wasm worker!\n");
#ifdef REPORT_RESULT
REPORT_RESULT(0);
#endif
void run_in_worker() {
emscripten_out("Hello from wasm worker!");
emscripten_wasm_worker_post_function_v(EMSCRIPTEN_WASM_WORKER_ID_PARENT, do_exit);
}

int main()
{
int main() {
emscripten_wasm_worker_t worker = emscripten_malloc_wasm_worker(/*stack size: */1024);
assert(worker);
emscripten_wasm_worker_post_function_v(worker, run_in_worker);
emscripten_exit_with_live_runtime();
}

0 comments on commit f53fb7a

Please sign in to comment.