Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/lib/libsigs.js
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,8 @@ sigs = {
_embind_register_value_object__sig: 'vpppppp',
_embind_register_value_object_field__sig: 'vpppppppppp',
_embind_register_void__sig: 'vpp',
_emscripten_create_wasm_worker__sig: 'ipi',
_emscripten_create_audio_worklet__sig: 'viipipp',
_emscripten_create_wasm_worker__sig: 'iipi',
_emscripten_dlopen_js__sig: 'vpppp',
_emscripten_dlsync_threads__sig: 'v',
_emscripten_fetch_get_response_headers__sig: 'pipp',
Expand Down Expand Up @@ -808,7 +809,6 @@ sigs = {
emscripten_stack_snapshot__sig: 'p',
emscripten_stack_unwind_buffer__sig: 'ippi',
emscripten_start_fetch__sig: 'vp',
emscripten_start_wasm_audio_worklet_thread_async__sig: 'vipipp',
emscripten_supports_offscreencanvas__sig: 'i',
emscripten_terminate_all_wasm_workers__sig: 'v',
emscripten_terminate_wasm_worker__sig: 'vi',
Expand Down
28 changes: 9 additions & 19 deletions src/lib/libwasm_worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
// This is the way that we signal to the Web Worker that it is hosting
// a Wasm Worker.
#if ASSERTIONS
'name': 'em-ww-' + _wasmWorkersID,
'name': 'em-ww-' + wwID,
#else
'name': 'em-ww',
#endif
Expand All @@ -62,16 +62,6 @@

addToLibrary({
$_wasmWorkers: {},
#if PTHREADS
// When the build contains both pthreads and Wasm Workers, offset the
// Wasm Worker ID space to avoid collisions with pthread TIDs (which start
// at 42). We use `1 << 21` since it's ~1/2 way through `pid_t` space,
// essentially giving pthreads the first 1/2 of the range and wasm workers the
// second half.
$_wasmWorkersID: {{{ 1 << 21 }}},
#else
$_wasmWorkersID: 1,
#endif

// Starting up a Wasm Worker is an asynchronous operation, hence if the parent
// thread performs any postMessage()-based wasm function calls to the
Expand Down Expand Up @@ -175,7 +165,7 @@ addToLibrary({
},

_emscripten_create_wasm_worker__deps: [
'$_wasmWorkers', '$_wasmWorkersID',
'$_wasmWorkers',
'$_wasmWorkerAppendToQueue', '$_wasmWorkerRunPostMessage',
#if ASSERTIONS
'emscripten_has_threading_support',
Expand All @@ -191,11 +181,11 @@ if (ENVIRONMENT_IS_WASM_WORKER
_wasmWorkers[0] = globalThis;
addEventListener("message", _wasmWorkerAppendToQueue);
}`,
_emscripten_create_wasm_worker: (stackLowestAddress, stackSize) => {
_emscripten_create_wasm_worker: (wwID, stackLowestAddress, stackSize) => {
#if ASSERTIONS
if (!_emscripten_has_threading_support()) {
err('create_wasm_worker: environment does not support SharedArrayBuffer, wasm workers are not available');
return 0;
return false;
}
#endif
let worker;
Expand All @@ -205,15 +195,15 @@ if (ENVIRONMENT_IS_WASM_WORKER
var p = trustedTypes.createPolicy(
'emscripten#workerPolicy1', { createScriptURL: (ignored) => {{{ wasmWorkerJs }}}}
);
worker = _wasmWorkers[_wasmWorkersID] = new Worker(p.createScriptURL('ignored'), {{{ wasmWorkerOptions }}});
worker = _wasmWorkers[wwID] = new Worker(p.createScriptURL('ignored'), {{{ wasmWorkerOptions }}});
} else
#endif
worker = _wasmWorkers[_wasmWorkersID] = new Worker({{{ wasmWorkerJs }}}, {{{ wasmWorkerOptions }}});
worker = _wasmWorkers[wwID] = new Worker({{{ wasmWorkerJs }}}, {{{ wasmWorkerOptions }}});
// Craft the Module object for the Wasm Worker scope:
worker.postMessage({
// Signal with a non-zero value that this Worker will be a Wasm Worker,
// and not the main browser thread.
wwID: _wasmWorkersID,
wwID,
wasm: wasmModule,
wasmMemory,
stackLowestAddress, // sb = stack bottom (lowest stack address, SP points at this when stack is full)
Expand All @@ -237,9 +227,9 @@ if (ENVIRONMENT_IS_WASM_WORKER
}
#endif
#if RUNTIME_DEBUG
dbg("done _emscripten_create_wasm_worker", _wasmWorkersID)
dbg("done _emscripten_create_wasm_worker", wwID)
#endif
return _wasmWorkersID++;
return true;
},

emscripten_terminate_wasm_worker: (id) => {
Expand Down
17 changes: 8 additions & 9 deletions src/lib/libwebaudio.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,16 +165,15 @@ var LibraryWebAudio = {
},

#if AUDIO_WORKLET
// emscripten_start_wasm_audio_worklet_thread_async() doesn't use stackAlloc,
// _emscripten_create_audio_worklet() doesn't use stackAlloc,
// etc., but the created worklet does.
emscripten_start_wasm_audio_worklet_thread_async__deps: [
'$_wasmWorkersID',
_emscripten_create_audio_worklet__deps: [
'$_emAudioDispatchProcessorCallback',
'$stackAlloc', '$stackRestore', '$stackSave'],
emscripten_start_wasm_audio_worklet_thread_async: (contextHandle, stackLowestAddress, stackSize, callback, userData) => {
_emscripten_create_audio_worklet: (wwID, contextHandle, stackLowestAddress, stackSize, callback, userData) => {

#if ASSERTIONS || WEBAUDIO_DEBUG
emAudioExpectContext(contextHandle, 'emscripten_start_wasm_audio_worklet_thread_async');
emAudioExpectContext(contextHandle, '_emscripten_create_audio_worklet');
#endif

var audioContext = emAudio[contextHandle];
Expand All @@ -190,12 +189,12 @@ var LibraryWebAudio = {
#endif

#if WEBAUDIO_DEBUG
dbg(`emscripten_start_wasm_audio_worklet_thread_async() adding audioworklet.js...`);
dbg(`_emscripten_create_audio_worklet() adding audioworklet.js...`);
#endif

var audioWorkletCreationFailed = () => {
#if ASSERTIONS || WEBAUDIO_DEBUG
dbg(`emscripten_start_wasm_audio_worklet_thread_async() addModule() failed!`);
dbg(`_emscripten_create_audio_worklet() addModule() failed!`);
#endif
{{{ makeDynCall('viip', 'callback') }}}(contextHandle, 0/*EM_FALSE*/, userData);
};
Expand All @@ -214,7 +213,7 @@ var LibraryWebAudio = {

audioWorklet.addModule({{{ wasmWorkerJs }}}).then(() => {
#if WEBAUDIO_DEBUG
dbg(`emscripten_start_wasm_audio_worklet_thread_async() addModule() completed`);
dbg(`_emscripten_create_audio_worklet() addModule() completed`);
#endif

#if MIN_FIREFOX_VERSION < 138 || MIN_CHROME_VERSION != TARGET_NOT_SUPPORTED || MIN_SAFARI_VERSION != TARGET_NOT_SUPPORTED
Expand Down Expand Up @@ -248,7 +247,7 @@ var LibraryWebAudio = {
// Assign the loaded AudioWorkletGlobalScope a Wasm Worker ID so that
// it can utilized its own TLS slots, and it is recognized to not be
// the main browser thread.
wwID: _wasmWorkersID++,
wwID,
#if MINIMAL_RUNTIME
wasm: Module['wasm'],
#else
Expand Down
5 changes: 4 additions & 1 deletion system/lib/libc/emscripten_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include <emscripten/em_macros.h>
#include <emscripten/proxying.h>
#include <emscripten/webaudio.h>
#include <emscripten/html5.h>
#include <emscripten/wasm_worker.h>

Expand Down Expand Up @@ -129,7 +130,9 @@ void emscripten_fetch_free(unsigned int);

// Internal implementation function in JavaScript side that emscripten_create_wasm_worker() calls to
// to perform the wasm worker creation.
emscripten_wasm_worker_t _emscripten_create_wasm_worker(void *stackLowestAddress, uint32_t stackSize);
bool _emscripten_create_wasm_worker(emscripten_wasm_worker_t wwID, void *stackLowestAddress, uint32_t stackSize);

void _emscripten_create_audio_worklet(emscripten_wasm_worker_t wwID, EMSCRIPTEN_WEBAUDIO_T audioContext, void *stackLowestAddress, uint32_t stackSize, EmscriptenStartWebAudioWorkletCallback callback, void *userData2);

void __resumeException(void* exn);
void __cxa_call_unexpected(void* exn);
Expand Down
31 changes: 31 additions & 0 deletions system/lib/pthread/emscripten_get_next_tid.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2026 The Emscripten Authors. All rights reserved.
* Emscripten is available under two separate licenses, the MIT license and the
* University of Illinois/NCSA Open Source License. Both these licenses can be
* found in the LICENSE file.
*/

#include <unistd.h>
#include <stdatomic.h>

#include "emscripten_internal.h"

// In case the stub syscall is not linked it
static int dummy_getpid(void) {
return 42;
}
weak_alias(dummy_getpid, __syscall_getpid);

static _Atomic pid_t next_tid = 0;

pid_t _emscripten_get_next_tid() {
// Create threads with monotonically increasing TID starting with the main
// thread which has TID == PID.
if (next_tid == 0) {
// Use CAS to initialize next_tid so that one thread will end up
// initializing it.
pid_t expected = 0;
atomic_compare_exchange_strong(&next_tid, &expected, getpid() + 1);
}
return next_tid++;
}
17 changes: 2 additions & 15 deletions system/lib/pthread/pthread_create.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <string.h>
#include <threads.h>
#include <unistd.h>

#include <emscripten/heap.h>
#include <emscripten/threading.h>

Expand Down Expand Up @@ -62,14 +63,6 @@ static void init_file_lock(FILE *f) {
if (f && f->lock<0) f->lock = 0;
}

static pid_t next_tid = 0;

// In case the stub syscall is not linked it
static int dummy_getpid(void) {
return 42;
}
weak_alias(dummy_getpid, __syscall_getpid);

static int tl_lock_count;
static int tl_lock_waiters;

Expand Down Expand Up @@ -121,12 +114,6 @@ int __pthread_create(pthread_t* restrict res,
return EINVAL;
}

// Create threads with monotonically increasing TID starting with the main
// thread which has TID == PID.
if (!next_tid) {
next_tid = getpid() + 1;
}

if (!libc.threaded) {
for (FILE *f=*__ofl_lock(); f; f=f->next)
init_file_lock(f);
Expand Down Expand Up @@ -177,7 +164,7 @@ int __pthread_create(pthread_t* restrict res,
// The pthread struct has a field that points to itself - this is used as a
// magic ID to detect whether the pthread_t structure is 'alive'.
new->self = new;
new->tid = next_tid++;
new->tid = _emscripten_get_next_tid();

// pthread struct robust_list head should point to itself.
new->robust_list.head = &new->robust_list.head;
Expand Down
2 changes: 2 additions & 0 deletions system/lib/pthread/threading_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,5 @@ void _emscripten_run_js_on_main_thread_done(void* ctx, void* arg, double result)
// if called from the main browser thread, this function will return zero
// since blocking is not allowed there).
int _emscripten_thread_supports_atomics_wait(void);

pid_t _emscripten_get_next_tid();
17 changes: 17 additions & 0 deletions system/lib/wasm_worker/audio_worklet.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright 2026 The Emscripten Authors. All rights reserved.
* Emscripten is available under two separate licenses, the MIT license and the
* University of Illinois/NCSA Open Source License. Both these licenses can be
* found in the LICENSE file.
*/

#include <emscripten/webaudio.h>

#include "emscripten_internal.h"
#include "threading_internal.h"

// Simple wrapper function around the JS _emscripten_create_audio_worklet
// function that adds the _emscripten_get_next_tid() as arg0
void emscripten_start_wasm_audio_worklet_thread_async(EMSCRIPTEN_WEBAUDIO_T audioContext, void *stackLowestAddress, uint32_t stackSize, EmscriptenStartWebAudioWorkletCallback callback, void *userData2) {
_emscripten_create_audio_worklet(_emscripten_get_next_tid(), audioContext, stackLowestAddress, stackSize, callback, userData2);
}
6 changes: 5 additions & 1 deletion system/lib/wasm_worker/library_wasm_worker.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "libc.h"
#include "stdio_impl.h"
#include "emscripten_internal.h"
#include "threading_internal.h"

#include <assert.h>
#include <emscripten/wasm_worker.h>
Expand Down Expand Up @@ -101,7 +102,10 @@ emscripten_wasm_worker_t emscripten_create_wasm_worker(void *stackPlusTLSAddress
// only going one way here.
if (!libc.threads_minus_1++) libc.need_locks = 1;

return _emscripten_create_wasm_worker(stackPlusTLSAddress, stackPlusTLSSize);
emscripten_wasm_worker_t wwID = _emscripten_get_next_tid();
if (!_emscripten_create_wasm_worker(wwID, stackPlusTLSAddress, stackPlusTLSSize))
return 0;
return wwID;
}

emscripten_wasm_worker_t emscripten_malloc_wasm_worker(size_t stackSize) {
Expand Down
Loading
Loading