Skip to content

Commit 5b85f91

Browse files
committed
Simplify thread clean / exit / terminate logic. NFC
Mostly inspired by musl.
1 parent 30b5029 commit 5b85f91

File tree

5 files changed

+31
-51
lines changed

5 files changed

+31
-51
lines changed

src/library_pthread.js

Lines changed: 16 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
var LibraryPThread = {
3030
$PThread__postset: 'PThread.init();',
3131
$PThread__deps: ['_emscripten_thread_init',
32-
'$killThread',
3332
'$cancelThread', '$cleanupThread', '$zeroMemory',
3433
'$spawnThread',
3534
'_emscripten_thread_free_data',
@@ -158,10 +157,7 @@ var LibraryPThread = {
158157
err('terminateAllThreads');
159158
#endif
160159
for (var t in PThread.pthreads) {
161-
var pthread = PThread.pthreads[t];
162-
if (pthread && pthread.worker) {
163-
PThread.returnWorkerToPool(pthread.worker);
164-
}
160+
cleanupThread(t, true);
165161
}
166162

167163
#if ASSERTIONS
@@ -181,7 +177,7 @@ var LibraryPThread = {
181177
}
182178
PThread.unusedWorkers = [];
183179
},
184-
returnWorkerToPool: function(worker) {
180+
returnWorkerToPool: function(worker, terminate) {
185181
// We don't want to run main thread queued calls here, since we are doing
186182
// some operations that leave the worker queue in an invalid state until
187183
// we are completely done (it would be bad if free() ends up calling a
@@ -190,9 +186,13 @@ var LibraryPThread = {
190186
// we are all done.
191187
var pthread_ptr = worker.pthread.threadInfoStruct;
192188
delete PThread.pthreads[pthread_ptr];
193-
// Note: worker is intentionally not terminated so the pool can
194-
// dynamically grow.
195-
PThread.unusedWorkers.push(worker);
189+
if (terminate) {
190+
worker.terminate();
191+
} else {
192+
// No termination needed? Return it to the worker pool as an
193+
// unused worker so the pool can dynamically grow.
194+
PThread.unusedWorkers.push(worker);
195+
}
196196
PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(worker), 1);
197197
// Not a running Worker anymore
198198
// Detach the worker from the pthread object, and return it to the
@@ -256,9 +256,7 @@ var LibraryPThread = {
256256
} else if (cmd === 'spawnThread') {
257257
spawnThread(d);
258258
} else if (cmd === 'cleanupThread') {
259-
cleanupThread(d['thread']);
260-
} else if (cmd === 'killThread') {
261-
killThread(d['thread']);
259+
cleanupThread(d['thread'], d['terminate']);
262260
} else if (cmd === 'cancelThread') {
263261
cancelThread(d['thread']);
264262
} else if (cmd === 'loaded') {
@@ -435,44 +433,24 @@ var LibraryPThread = {
435433
}
436434
},
437435

438-
$killThread__deps: ['_emscripten_thread_free_data'],
439-
$killThread: function(pthread_ptr) {
440-
#if PTHREADS_DEBUG
441-
err('killThread ' + ptrToString(pthread_ptr));
442-
#endif
443-
#if ASSERTIONS
444-
assert(!ENVIRONMENT_IS_PTHREAD, 'Internal Error! killThread() can only ever be called from main application thread!');
445-
assert(pthread_ptr, 'Internal Error! Null pthread_ptr in killThread!');
446-
#endif
447-
var pthread = PThread.pthreads[pthread_ptr];
448-
delete PThread.pthreads[pthread_ptr];
449-
pthread.worker.terminate();
450-
__emscripten_thread_free_data(pthread_ptr);
451-
// The worker was completely nuked (not just the pthread execution it was hosting), so remove it from running workers
452-
// but don't put it back to the pool.
453-
PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(pthread.worker), 1); // Not a running Worker anymore.
454-
pthread.worker.pthread = undefined;
455-
},
456-
457436
__emscripten_thread_cleanup: function(thread) {
458437
// Called when a thread needs to be cleaned up so it can be reused.
459438
// A thread is considered reusable when it either returns from its
460439
// entry point, calls pthread_exit, or acts upon a cancellation.
461440
// Detached threads are responsible for calling this themselves,
462441
// otherwise pthread_join is responsible for calling this.
463-
if (!ENVIRONMENT_IS_PTHREAD) cleanupThread(thread);
464-
else postMessage({ 'cmd': 'cleanupThread', 'thread': thread });
442+
if (!ENVIRONMENT_IS_PTHREAD) cleanupThread(thread, false);
443+
else postMessage({ 'cmd': 'cleanupThread', 'thread': thread, 'terminate': false });
465444
},
466445

467-
$cleanupThread: function(pthread_ptr) {
446+
$cleanupThread: function(pthread_ptr, terminate) {
468447
#if ASSERTIONS
469448
assert(!ENVIRONMENT_IS_PTHREAD, 'Internal Error! cleanupThread() can only ever be called from main application thread!');
470449
assert(pthread_ptr, 'Internal Error! Null pthread_ptr in cleanupThread!');
471450
#endif
472451
var pthread = PThread.pthreads[pthread_ptr];
473452
assert(pthread);
474-
var worker = pthread.worker;
475-
PThread.returnWorkerToPool(worker);
453+
PThread.returnWorkerToPool(pthread.worker, terminate);
476454
},
477455

478456
#if MAIN_MODULE
@@ -799,8 +777,8 @@ var LibraryPThread = {
799777
if (!ENVIRONMENT_IS_PTHREAD) cancelThread(thread);
800778
else postMessage({ 'cmd': 'cancelThread', 'thread': thread });
801779
} else {
802-
if (!ENVIRONMENT_IS_PTHREAD) killThread(thread);
803-
else postMessage({ 'cmd': 'killThread', 'thread': thread });
780+
if (!ENVIRONMENT_IS_PTHREAD) cleanupThread(thread, true);
781+
else postMessage({ 'cmd': 'cleanupThread', 'thread': thread, 'terminate': true });
804782
}
805783
return 0;
806784
},

system/lib/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ tid_t GetTid() {
557557
#elif SANITIZER_SOLARIS
558558
return thr_self();
559559
#elif SANITIZER_EMSCRIPTEN
560-
return (tid_t) pthread_self();
560+
return gettid();
561561
#else
562562
return internal_syscall(SYSCALL(gettid));
563563
#endif

system/lib/libc/musl/src/thread/pthread_detach.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ static int __pthread_detach(pthread_t t)
66
#ifdef __EMSCRIPTEN__
77
// XXX EMSCRIPTEN: Add check for invalid (already joined) thread. Again
88
// for the benefit of the conformance tests.
9-
if (!_emscripten_thread_is_valid(t))
10-
return ESRCH;
9+
if (!_emscripten_thread_is_valid(t)) return ESRCH;
1110
#endif
1211
/* If the cas fails, detach state is either already-detached
1312
* or exiting/exited, and pthread_join will trap or cleanup. */

system/lib/pthread/library_pthread.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,7 @@ int emscripten_dispatch_to_thread_async_(pthread_t target_thread,
639639
}
640640

641641
int _emscripten_thread_is_valid(pthread_t thread) {
642-
return thread->self == thread;
642+
return thread->tid;
643643
}
644644

645645
static void *dummy_tsd[1] = { 0 };
@@ -650,8 +650,7 @@ __attribute__((constructor(48)))
650650
void __emscripten_init_main_thread(void) {
651651
__emscripten_init_main_thread_js(&__main_pthread);
652652

653-
// The pthread struct has a field that points to itself - this is used as
654-
// a magic ID to detect whether the pthread_t structure is 'alive'.
653+
// The pthread struct has a field that points to itself.
655654
__main_pthread.self = &__main_pthread;
656655
__main_pthread.stack = (void*)emscripten_stack_get_base();
657656
__main_pthread.stack_size = emscripten_stack_get_base() - emscripten_stack_get_end();

system/lib/pthread/pthread_create.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,10 @@ int __pthread_create(pthread_t* restrict res,
135135
new->map_base = block;
136136
new->map_size = size;
137137

138-
// The pthread struct has a field that points to itself - this is used as a
139-
// magic ID to detect whether the pthread_t structure is 'alive'.
138+
// The pthread struct has a field that points to itself.
140139
new->self = new;
140+
141+
// Thread ID, this becomes zero when the thread is no longer available.
141142
new->tid = next_tid++;
142143

143144
// pthread struct robust_list head should point to itself.
@@ -226,20 +227,23 @@ void _emscripten_thread_free_data(pthread_t t) {
226227
// A thread can never free its own thread data.
227228
assert(t != pthread_self());
228229
#ifndef NDEBUG
229-
if (t->profilerBlock) {
230-
emscripten_builtin_free(t->profilerBlock);
230+
intptr_t old_block = __c11_atomic_exchange((_Atomic intptr_t*)&t->profilerBlock, 0, __ATOMIC_SEQ_CST);
231+
if (old_block) {
232+
emscripten_builtin_free((void*)old_block);
231233
}
232234
#endif
235+
// The tid may be reused. Clear it to prevent inadvertent use
236+
// and inform functions that would use it that it's no longer
237+
// available.
238+
t->tid = 0;
233239

234-
// Free all the enture thread block (called map_base because
240+
// Free the entire thread block (called map_base because
235241
// musl normally allocates this using mmap). This region
236242
// includes the pthread structure itself.
237243
unsigned char* block = t->map_base;
238244
#ifdef PTHREAD_DEBUG
239245
_emscripten_errf("_emscripten_thread_free_data thread=%p map_base=%p", t, block);
240246
#endif
241-
// To aid in debugging, set the entire region to zero.
242-
memset(block, 0, sizeof(struct pthread));
243247
emscripten_builtin_free(block);
244248
}
245249

0 commit comments

Comments
 (0)