SHARED_WASMGC mechanism for sending data to new threads - #27472
Conversation
The experimental SHARED_WASMGC setting already allows for sharing data between threads in the form of a single global value initialized once on the main thread and then imported whenever the module is instantiated on a new worker. This is technically sufficient to bootstrap arbitrary shared state, including a user space message-passing primitives, as long as threads can be initialized with different thread IDs, but this is not very convenient. Add another mechanism gated behind SHARED_WASMGC that allows sending specific shared values to newly spawned threads without a complicated user space TID-keyed parallel mailbox system. The new system depends on the module exporting two new globals, _gc_spawn_arg for outgoing values and _gc_thread_state for received values. When a thread calls pthread_create, its _gc_spawn_arg value is read and transferred via postMessage to the Worker where the new thread is created, where it is assigned to _gc_thread_state before pthread initialization. Make both the existing globally shared state mechanism and the new thread state mechanism optional so users can use either or both as they wish.
| #endif | ||
| #if SHARED_WASMGC | ||
| sharedHeapRootVal: wasmExports['_shared_heap_root'].value, | ||
| sharedHeapRootVal: wasmExports['_shared_heap_root']?.value ?? null, |
There was a problem hiding this comment.
Do you need the ?? null it seems redundant to because if the null would only used if the LHS is already null-ish.
There was a problem hiding this comment.
If we didn't have the ?? and there was no export, this would end up sending undefined. But then I guess the receiving thread would not do anything with the undefined, so it would be ok. I'll simplify this.
| #endif // OFFSCREENCANVAS_SUPPORT | ||
|
|
||
| #if SHARED_WASMGC | ||
| var gcSpawnArg = wasmExports['_gc_spawn_arg']?.value ?? null; |
| if (ENVIRONMENT_IS_PTHREAD && (!transferList.length || error)) { | ||
| if (ENVIRONMENT_IS_PTHREAD && (!transferList.length || error) | ||
| #if SHARED_WASMGC | ||
| && gcSpawnArg === null |
There was a problem hiding this comment.
Does gcSpawnArg need to go in transferList? Is so then maybe this change is not needed?
There was a problem hiding this comment.
No, it can just be added as a normal property of the message.
| #endif | ||
| #if SHARED_WASMGC | ||
| if (wasmExports['_gc_thread_state']) { | ||
| wasmExports['_gc_thread_state'].value = msgData.gcSpawnArg ?? null; |
There was a problem hiding this comment.
Do you need the ?? null here?
| ) | ||
| ''') | ||
|
|
||
| out_js = self.in_dir('test_shared_wasmgc_thread_state.js') |
There was a problem hiding this comment.
You don't (or at least you shouldn't) need self.in_dir here
| out_wasm = self.in_dir('test_shared_wasmgc_thread_state.wasm') | ||
|
|
||
| self.run_process([ | ||
| EMCC, '-pthread', '-sSHARED_WASMGC', '-sERROR_ON_UNDEFINED_SYMBOLS=0', |
There was a problem hiding this comment.
Why is ERROR_ON_UNDEFINED_SYMBOLS=0 needed?
| self.run_process([ | ||
| EMCC, '-pthread', '-sSHARED_WASMGC', '-sERROR_ON_UNDEFINED_SYMBOLS=0', | ||
| '-sEXIT_RUNTIME', '-sPROXY_TO_PTHREAD', | ||
| '-sEXPORTED_FUNCTIONS=_main,_print_int,_spawn_pthread,_join_pthread', |
There was a problem hiding this comment.
Instead of EXPORTED_FUNCTIONS you can add `EMSCRIPTEN_KEEPALIVE in the source
The experimental SHARED_WASMGC setting already allows for sharing data between threads in the form of a single global value initialized once on the main thread and then imported whenever the module is instantiated on a new worker. This is technically sufficient to bootstrap arbitrary shared state, including a user space message-passing primitives, as long as threads can be initialized with different thread IDs, but this is not very convenient.
Add another mechanism gated behind SHARED_WASMGC that allows sending specific shared values to newly spawned threads without a complicated user space TID-keyed parallel mailbox system. The new system depends on the module exporting two new globals, _gc_spawn_arg for outgoing values and _gc_thread_state for received values. When a thread calls pthread_create, its _gc_spawn_arg value is read and transferred via postMessage to the Worker where the new thread is created, where it is assigned to _gc_thread_state before pthread initialization.
Make both the existing globally shared state mechanism and the new thread state mechanism optional so users can use either or both as they wish.