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
6 changes: 6 additions & 0 deletions src/lib/libpthread.js
Original file line number Diff line number Diff line change
Expand Up @@ -1219,6 +1219,12 @@ var LibraryPThread = {
},

$markAsFinished: (pthread_ptr) => {
// When this is reached via a message from the exiting thread, the thread
// may already have been joined and cleaned up, and its pthread_t reused by
// a new thread that is still running, which must not be marked.
if (!PThread.pthreads[pthread_ptr]) return;
var state = {{{ makeGetValue('pthread_ptr', C_STRUCTS.pthread.detach_state, 'i32') }}};
if (state != {{{ cDefs.DT_EXITING }}} && state != {{{ cDefs.DT_EXITED }}}) return;
#if PTHREADS_DEBUG
dbg(`markAsFinished: ${ptrToString(pthread_ptr)}`);
#endif
Expand Down
3 changes: 3 additions & 0 deletions src/struct_info_generated.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
"CSIZE": 48,
"DT_CHR": 2,
"DT_DIR": 4,
"DT_EXITED": 0,
"DT_EXITING": 1,
"DT_LNK": 10,
"DT_REG": 8,
"E2BIG": 1,
Expand Down Expand Up @@ -1078,6 +1080,7 @@
},
"pthread": {
"__size__": 120,
"detach_state": 28,
"profilerBlock": 96,
"stack": 48,
"stack_size": 52,
Expand Down
3 changes: 3 additions & 0 deletions src/struct_info_generated_wasm64.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
"CSIZE": 48,
"DT_CHR": 2,
"DT_DIR": 4,
"DT_EXITED": 0,
"DT_EXITING": 1,
"DT_LNK": 10,
"DT_REG": 8,
"E2BIG": 1,
Expand Down Expand Up @@ -1078,6 +1080,7 @@
},
"pthread": {
"__size__": 216,
"detach_state": 52,
"profilerBlock": 176,
"stack": 80,
"stack_size": 88,
Expand Down
5 changes: 5 additions & 0 deletions src/struct_info_internal.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@
// ===========================================
{
"file": "pthread_impl.h",
"defines": [
"DT_EXITED",
"DT_EXITING"
],
"structs": {
"pthread": [
"detach_state",
"profilerBlock",
"stack",
"stack_size",
Expand Down
70 changes: 70 additions & 0 deletions test/core/pthread/test_pthread_dlsync_reused.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// A joinable thread's "finished" message only reaches the main thread once it
// returns to the event loop. By then the main thread has joined that thread
// and created a new one that reuses its pthread_t. The late message must not
// stop the new, still running thread from taking part in dlopen()'s sync.

#include <assert.h>
#include <dlfcn.h>
#include <emscripten/eventloop.h>
#include <pthread.h>
#include <sched.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

typedef int (*func_t)();

pthread_t waiter;
atomic_bool waiter_released;
func_t (*get_side_func)();
int result;

void* noop(void* arg) { return NULL; }

void* wait_then_call(void* arg) {
// sched_yield() also runs any pending dlopen() sync for this thread.
while (!waiter_released) {
sched_yield();
}
if (get_side_func) {
result = get_side_func()();
}
return NULL;
}

void release_waiter() {
waiter_released = true;
pthread_join(waiter, NULL);
waiter_released = false;
}

void load_and_call(void* arg) {
void* handle = dlopen("libside.so", RTLD_NOW);
assert(handle);
get_side_func = dlsym(handle, "side_func_address");
assert(get_side_func);
// The waiter traps here if dlopen() skipped it.
release_waiter();
assert(result == 43);
printf("done\n");
exit(0);
}

int main() {
// The new thread almost always gets the joined thread's block back, but
// retry rather than depend on it.
for (int i = 0; i < 10; i++) {
pthread_t joined;
pthread_create(&joined, NULL, noop, NULL);
pthread_join(joined, NULL);
pthread_create(&waiter, NULL, wait_then_call, NULL);
if (pthread_equal(waiter, joined)) {
emscripten_set_timeout(load_and_call, 100, NULL);
return 0;
}
release_waiter();
}
printf("no thread reused a joined thread's pthread_t\n");
return 1;
}
9 changes: 9 additions & 0 deletions test/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -9394,6 +9394,15 @@ def test_pthread_dlopen(self, args):
['side module ctor', 'done\n', 'side module atexit'],
assert_all=True, cflags=args)

@needs_dylink
@requires_pthreads
def test_pthread_dlsync_reused(self):
self.cflags += ['-Wno-experimental', '-pthread']
self.build_dlfcn_lib(test_file('core/pthread/test_pthread_dlopen_side.c'))
self.cflags += ['--embed-file', 'libside.so@libside.so']
self.prep_dlfcn_main()
self.do_runf('core/pthread/test_pthread_dlsync_reused.c', 'done\n', cflags=['-sEXIT_RUNTIME'])

@needs_dylink
@requires_pthreads
@flaky('https://github.com/emscripten-core/emscripten/issues/18887')
Expand Down
Loading