Skip to content

[emval] Gate val thread-affinity check on __EMSCRIPTEN_PTHREADS__ instead of _REENTRANT#27109

Open
iakovgi wants to merge 4 commits into
emscripten-core:mainfrom
iakovgi:fix/val-thread-assertion-gate
Open

[emval] Gate val thread-affinity check on __EMSCRIPTEN_PTHREADS__ instead of _REENTRANT#27109
iakovgi wants to merge 4 commits into
emscripten-core:mainfrom
iakovgi:fix/val-thread-assertion-gate

Conversation

@iakovgi

@iakovgi iakovgi commented Jun 12, 2026

Copy link
Copy Markdown

Summary

Replace #ifdef _REENTRANT with #ifdef __EMSCRIPTEN_PTHREADS__ in the two guarded sites in val.h.

Motivation

_REENTRANT is a POSIX convention meaning "use reentrant libc variants." It does not guarantee that the pthread runtime is present. Third-party libraries (e.g. Poco) set it as a PUBLIC compile definition for any Unix-like target, and Emscripten qualifies. This leaks _REENTRANT into consumer TUs via transitive CMake dependencies.

The val::thread field is declared unconditionally (to keep struct layout stable, see #26508 (comment)), but the constructor only initializes it under #ifdef _REENTRANT. In a non-pthread build where some TUs have _REENTRANT (from library deps) and others don't (standalone libraries built without those deps), vals cross the boundary with an uninitialized thread field. The assertion then compares garbage against pthread_self() and fires spuriously.

__EMSCRIPTEN_PTHREADS__ is compiler-set (only with -pthread), cannot be leaked by user code, and -pthread always implies _REENTRANT anyway - so there is no configuration where the check is needed but __EMSCRIPTEN_PTHREADS__ is absent.

…tead of _REENTRANT

`_REENTRANT` is a POSIX convention that third-party libraries may define unconditionally for Unix-like targets (including Emscripten) without implying that the pthread runtime is actually present. When some translation units in a non-pthread build inherit `_REENTRANT` via transitive library dependencies and others don't, the `val::thread` field is only initialized in TUs that see `_REENTRANT`. Vals constructed without it have an uninitialized `thread` field; accessing them from TUs with `_REENTRANT` triggers a spurious assertion failure.

`__EMSCRIPTEN_PTHREADS__` is set by the compiler only when `-pthread` is passed. It cannot be leaked by user code or build-system definitions, and precisely indicates that the pthread runtime is linked and `pthread_self()` returns meaningful values. Since `-pthread` always implies `_REENTRANT` anyway, there is no configuration where the assertion would be needed but `__EMSCRIPTEN_PTHREADS__` is absent.
@sbc100

sbc100 commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Hm, thats interesting. I always thought _REENTRANT was basically the only way to detect the -pthread flag was using in gcc and clang. Indeed IIUC this is really the only thing that the -pthread flag does when passed to clang/gcc.

I didn't think its was intended to be set by users. Could you point me to the usages in Poco, glib?

@iakovgi

iakovgi commented Jun 12, 2026

Copy link
Copy Markdown
Author

@sbc100

sbc100 commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

@sbc100 In our use case the _REENTRANT came from here: https://github.com/pocoproject/poco/blob/501ddd6608ea3fcdfcf2580e4c10787f64b08559/Foundation/CMakeLists.txt#L175.

I think they could be wrong here to define _REENTRANT like that.. from all I can find on this macro its supposed to be automatically defined by -pthread. Just like __EMSCRIPTEN_PTHREADS__ its supposed to be defined by the compiler I think.

But that doesn't mean to shouldn't necessarily land this change of course.

@sbc100 sbc100 enabled auto-merge (squash) June 12, 2026 21:24
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.

2 participants