Skip to content

[pthread] Don't let a stale "finished" message mark a reused thread in dlopen sync - #27846

Open
typedrat wants to merge 1 commit into
emscripten-core:mainfrom
typedrat:typedrat/pthread-finished-mark-reused
Open

typedrat wants to merge 1 commit into
emscripten-core:mainfrom
typedrat:typedrat/pthread-finished-mark-reused

Conversation

@typedrat

@typedrat typedrat commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

With dynamic linking, a joinable thread posts CMD_MARK_AS_FINISHED to the main thread as it exits, so that dlopen()'s sync skips it. But a join cleans the thread up as soon as it returns, possibly before that message is handled, and a new thread created in the meantime can get the same pthread_t. The late message then marks the new, running thread as finished, so later dlopen() calls skip it, and its calls into newly loaded modules trap with "table index is out of bounds".

The fix only marks a thread if it is still known and has actually exited (detach_state is DT_EXITING or DT_EXITED). The check lives in markAsFinished, which reads detach_state directly via C_STRUCTS.pthread.

Test: core.test_pthread_dlsync_reused joins a thread on the main thread, creates another one that reuses its pthread_t, then dlopen()s a side module and calls into it from the new thread. It fails on main with the trap and passes with the fix, in core0–core3 and wasm64. It assumes the stale message is handled within the 100 ms before dlopen(); if it weren't, the test could pass without the fix, but it can't fail with it.

Fixes: #27841

@clementperon

Copy link
Copy Markdown
Contributor

@sbc100 I reach to a similar patch.

And I repro on 6.0.9 with Kodi-style thread churn + dlopen(): 62/140 runs trap without this change, 0/140 with it.

Needs a rebase after #27999.

Comment thread src/lib/libpthread.js Outdated
Comment thread test/test_core.py Outdated
Comment thread test/core/pthread/test_pthread_dlsync_reused.c Outdated
Comment thread test/core/pthread/test_pthread_dlsync_reused.c Outdated
@typedrat
typedrat force-pushed the typedrat/pthread-finished-mark-reused branch from e20a4dd to f6612c0 Compare October 23, 2026 20:18
…n dlopen sync

With dynamic linking, a joinable thread posts CMD_MARK_AS_FINISHED to the
main thread as it exits, so that dlopen()'s sync skips it. A join cleans the
thread up as soon as it returns, possibly before that message is handled,
and a new thread created in the meantime can get the same pthread_t. The
late message then marked the new, running thread as finished, so every
later dlopen() skipped it and its calls into newly loaded modules trapped.

Only mark a thread if it is still known and has actually exited.

Fixes: emscripten-core#27841
@typedrat
typedrat force-pushed the typedrat/pthread-finished-mark-reused branch from f6612c0 to cf37052 Compare October 23, 2026 20:24

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dlopen() skips a running thread whose pthread_t was reused from a just-joined thread ("table index is out of bounds")

3 participants