Skip to content

Commit 6ea9c28

Browse files
authored
Unify thread ID allocation between Wasm Workers and pthreads. NFC (#26670)
Followup to #26660 and #26472
1 parent ef94d08 commit 6ea9c28

17 files changed

+264
-230
lines changed

src/lib/libsigs.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,8 @@ sigs = {
313313
_embind_register_value_object__sig: 'vpppppp',
314314
_embind_register_value_object_field__sig: 'vpppppppppp',
315315
_embind_register_void__sig: 'vpp',
316-
_emscripten_create_wasm_worker__sig: 'ipi',
316+
_emscripten_create_audio_worklet__sig: 'viipipp',
317+
_emscripten_create_wasm_worker__sig: 'iipi',
317318
_emscripten_dlopen_js__sig: 'vpppp',
318319
_emscripten_dlsync_threads__sig: 'v',
319320
_emscripten_fetch_get_response_headers__sig: 'pipp',
@@ -808,7 +809,6 @@ sigs = {
808809
emscripten_stack_snapshot__sig: 'p',
809810
emscripten_stack_unwind_buffer__sig: 'ippi',
810811
emscripten_start_fetch__sig: 'vp',
811-
emscripten_start_wasm_audio_worklet_thread_async__sig: 'vipipp',
812812
emscripten_supports_offscreencanvas__sig: 'i',
813813
emscripten_terminate_all_wasm_workers__sig: 'v',
814814
emscripten_terminate_wasm_worker__sig: 'vi',

src/lib/libwasm_worker.js

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
// This is the way that we signal to the Web Worker that it is hosting
5151
// a Wasm Worker.
5252
#if ASSERTIONS
53-
'name': 'em-ww-' + _wasmWorkersID,
53+
'name': 'em-ww-' + wwID,
5454
#else
5555
'name': 'em-ww',
5656
#endif
@@ -62,16 +62,6 @@
6262

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

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

177167
_emscripten_create_wasm_worker__deps: [
178-
'$_wasmWorkers', '$_wasmWorkersID',
168+
'$_wasmWorkers',
179169
'$_wasmWorkerAppendToQueue', '$_wasmWorkerRunPostMessage',
180170
#if ASSERTIONS
181171
'emscripten_has_threading_support',
@@ -191,11 +181,11 @@ if (ENVIRONMENT_IS_WASM_WORKER
191181
_wasmWorkers[0] = globalThis;
192182
addEventListener("message", _wasmWorkerAppendToQueue);
193183
}`,
194-
_emscripten_create_wasm_worker: (stackLowestAddress, stackSize) => {
184+
_emscripten_create_wasm_worker: (wwID, stackLowestAddress, stackSize) => {
195185
#if ASSERTIONS
196186
if (!_emscripten_has_threading_support()) {
197187
err('create_wasm_worker: environment does not support SharedArrayBuffer, wasm workers are not available');
198-
return 0;
188+
return false;
199189
}
200190
#endif
201191
let worker;
@@ -205,15 +195,15 @@ if (ENVIRONMENT_IS_WASM_WORKER
205195
var p = trustedTypes.createPolicy(
206196
'emscripten#workerPolicy1', { createScriptURL: (ignored) => {{{ wasmWorkerJs }}}}
207197
);
208-
worker = _wasmWorkers[_wasmWorkersID] = new Worker(p.createScriptURL('ignored'), {{{ wasmWorkerOptions }}});
198+
worker = _wasmWorkers[wwID] = new Worker(p.createScriptURL('ignored'), {{{ wasmWorkerOptions }}});
209199
} else
210200
#endif
211-
worker = _wasmWorkers[_wasmWorkersID] = new Worker({{{ wasmWorkerJs }}}, {{{ wasmWorkerOptions }}});
201+
worker = _wasmWorkers[wwID] = new Worker({{{ wasmWorkerJs }}}, {{{ wasmWorkerOptions }}});
212202
// Craft the Module object for the Wasm Worker scope:
213203
worker.postMessage({
214204
// Signal with a non-zero value that this Worker will be a Wasm Worker,
215205
// and not the main browser thread.
216-
wwID: _wasmWorkersID,
206+
wwID,
217207
wasm: wasmModule,
218208
wasmMemory,
219209
stackLowestAddress, // sb = stack bottom (lowest stack address, SP points at this when stack is full)
@@ -237,9 +227,9 @@ if (ENVIRONMENT_IS_WASM_WORKER
237227
}
238228
#endif
239229
#if RUNTIME_DEBUG
240-
dbg("done _emscripten_create_wasm_worker", _wasmWorkersID)
230+
dbg("done _emscripten_create_wasm_worker", wwID)
241231
#endif
242-
return _wasmWorkersID++;
232+
return true;
243233
},
244234

245235
emscripten_terminate_wasm_worker: (id) => {

src/lib/libwebaudio.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -165,16 +165,15 @@ var LibraryWebAudio = {
165165
},
166166

167167
#if AUDIO_WORKLET
168-
// emscripten_start_wasm_audio_worklet_thread_async() doesn't use stackAlloc,
168+
// _emscripten_create_audio_worklet() doesn't use stackAlloc,
169169
// etc., but the created worklet does.
170-
emscripten_start_wasm_audio_worklet_thread_async__deps: [
171-
'$_wasmWorkersID',
170+
_emscripten_create_audio_worklet__deps: [
172171
'$_emAudioDispatchProcessorCallback',
173172
'$stackAlloc', '$stackRestore', '$stackSave'],
174-
emscripten_start_wasm_audio_worklet_thread_async: (contextHandle, stackLowestAddress, stackSize, callback, userData) => {
173+
_emscripten_create_audio_worklet: (wwID, contextHandle, stackLowestAddress, stackSize, callback, userData) => {
175174

176175
#if ASSERTIONS || WEBAUDIO_DEBUG
177-
emAudioExpectContext(contextHandle, 'emscripten_start_wasm_audio_worklet_thread_async');
176+
emAudioExpectContext(contextHandle, '_emscripten_create_audio_worklet');
178177
#endif
179178

180179
var audioContext = emAudio[contextHandle];
@@ -190,12 +189,12 @@ var LibraryWebAudio = {
190189
#endif
191190

192191
#if WEBAUDIO_DEBUG
193-
dbg(`emscripten_start_wasm_audio_worklet_thread_async() adding audioworklet.js...`);
192+
dbg(`_emscripten_create_audio_worklet() adding audioworklet.js...`);
194193
#endif
195194

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

215214
audioWorklet.addModule({{{ wasmWorkerJs }}}).then(() => {
216215
#if WEBAUDIO_DEBUG
217-
dbg(`emscripten_start_wasm_audio_worklet_thread_async() addModule() completed`);
216+
dbg(`_emscripten_create_audio_worklet() addModule() completed`);
218217
#endif
219218

220219
#if MIN_FIREFOX_VERSION < 138 || MIN_CHROME_VERSION != TARGET_NOT_SUPPORTED || MIN_SAFARI_VERSION != TARGET_NOT_SUPPORTED
@@ -248,7 +247,7 @@ var LibraryWebAudio = {
248247
// Assign the loaded AudioWorkletGlobalScope a Wasm Worker ID so that
249248
// it can utilized its own TLS slots, and it is recognized to not be
250249
// the main browser thread.
251-
wwID: _wasmWorkersID++,
250+
wwID,
252251
#if MINIMAL_RUNTIME
253252
wasm: Module['wasm'],
254253
#else

system/lib/libc/emscripten_internal.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include <emscripten/em_macros.h>
1717
#include <emscripten/proxying.h>
18+
#include <emscripten/webaudio.h>
1819
#include <emscripten/html5.h>
1920
#include <emscripten/wasm_worker.h>
2021

@@ -134,7 +135,9 @@ void emscripten_fetch_free(unsigned int);
134135

135136
// Internal implementation function in JavaScript side that emscripten_create_wasm_worker() calls to
136137
// to perform the wasm worker creation.
137-
emscripten_wasm_worker_t _emscripten_create_wasm_worker(void *stackLowestAddress, uint32_t stackSize);
138+
bool _emscripten_create_wasm_worker(emscripten_wasm_worker_t wwID, void *stackLowestAddress, uint32_t stackSize);
139+
140+
void _emscripten_create_audio_worklet(emscripten_wasm_worker_t wwID, EMSCRIPTEN_WEBAUDIO_T audioContext, void *stackLowestAddress, uint32_t stackSize, EmscriptenStartWebAudioWorkletCallback callback, void *userData2);
138141

139142
void __resumeException(void* exn);
140143
void __cxa_call_unexpected(void* exn);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2026 The Emscripten Authors. All rights reserved.
3+
* Emscripten is available under two separate licenses, the MIT license and the
4+
* University of Illinois/NCSA Open Source License. Both these licenses can be
5+
* found in the LICENSE file.
6+
*/
7+
8+
#include <unistd.h>
9+
#include <stdatomic.h>
10+
11+
#include "emscripten_internal.h"
12+
13+
// In case the stub syscall is not linked it
14+
static int dummy_getpid(void) {
15+
return 42;
16+
}
17+
weak_alias(dummy_getpid, __syscall_getpid);
18+
19+
static _Atomic pid_t next_tid = 0;
20+
21+
pid_t _emscripten_get_next_tid() {
22+
// Create threads with monotonically increasing TID starting with the main
23+
// thread which has TID == PID.
24+
if (next_tid == 0) {
25+
// Use CAS to initialize next_tid so that one thread will end up
26+
// initializing it.
27+
pid_t expected = 0;
28+
atomic_compare_exchange_strong(&next_tid, &expected, getpid() + 1);
29+
}
30+
return next_tid++;
31+
}

system/lib/pthread/pthread_create.c

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <string.h>
1515
#include <threads.h>
1616
#include <unistd.h>
17+
1718
#include <emscripten/heap.h>
1819
#include <emscripten/threading.h>
1920

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

65-
static pid_t next_tid = 0;
66-
67-
// In case the stub syscall is not linked it
68-
static int dummy_getpid(void) {
69-
return 42;
70-
}
71-
weak_alias(dummy_getpid, __syscall_getpid);
72-
7366
static int tl_lock_count;
7467
static int tl_lock_waiters;
7568

@@ -121,12 +114,6 @@ int __pthread_create(pthread_t* restrict res,
121114
return EINVAL;
122115
}
123116

124-
// Create threads with monotonically increasing TID starting with the main
125-
// thread which has TID == PID.
126-
if (!next_tid) {
127-
next_tid = getpid() + 1;
128-
}
129-
130117
if (!libc.threaded) {
131118
for (FILE *f=*__ofl_lock(); f; f=f->next)
132119
init_file_lock(f);
@@ -177,7 +164,7 @@ int __pthread_create(pthread_t* restrict res,
177164
// The pthread struct has a field that points to itself - this is used as a
178165
// magic ID to detect whether the pthread_t structure is 'alive'.
179166
new->self = new;
180-
new->tid = next_tid++;
167+
new->tid = _emscripten_get_next_tid();
181168

182169
// pthread struct robust_list head should point to itself.
183170
new->robust_list.head = &new->robust_list.head;

system/lib/pthread/threading_internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,5 @@ void _emscripten_run_js_on_main_thread_done(void* ctx, void* arg, double result)
104104
// if called from the main browser thread, this function will return zero
105105
// since blocking is not allowed there).
106106
int _emscripten_thread_supports_atomics_wait(void);
107+
108+
pid_t _emscripten_get_next_tid();
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright 2026 The Emscripten Authors. All rights reserved.
3+
* Emscripten is available under two separate licenses, the MIT license and the
4+
* University of Illinois/NCSA Open Source License. Both these licenses can be
5+
* found in the LICENSE file.
6+
*/
7+
8+
#include <emscripten/webaudio.h>
9+
10+
#include "emscripten_internal.h"
11+
#include "threading_internal.h"
12+
13+
// Simple wrapper function around the JS _emscripten_create_audio_worklet
14+
// function that adds the _emscripten_get_next_tid() as arg0
15+
void emscripten_start_wasm_audio_worklet_thread_async(EMSCRIPTEN_WEBAUDIO_T audioContext, void *stackLowestAddress, uint32_t stackSize, EmscriptenStartWebAudioWorkletCallback callback, void *userData2) {
16+
_emscripten_create_audio_worklet(_emscripten_get_next_tid(), audioContext, stackLowestAddress, stackSize, callback, userData2);
17+
}

system/lib/wasm_worker/library_wasm_worker.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "libc.h"
1515
#include "stdio_impl.h"
1616
#include "emscripten_internal.h"
17+
#include "threading_internal.h"
1718

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

104-
return _emscripten_create_wasm_worker(stackPlusTLSAddress, stackPlusTLSSize);
105+
emscripten_wasm_worker_t wwID = _emscripten_get_next_tid();
106+
if (!_emscripten_create_wasm_worker(wwID, stackPlusTLSAddress, stackPlusTLSSize))
107+
return 0;
108+
return wwID;
105109
}
106110

107111
emscripten_wasm_worker_t emscripten_malloc_wasm_worker(size_t stackSize) {

0 commit comments

Comments
 (0)