-
Notifications
You must be signed in to change notification settings - Fork 10
Crash fix in case of forks: remove usage of pthread_once #483
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
When compiling on Alpine Linux (musl libc) and running on glibc systems, pthread_once has incompatible internal state machines that cause crashes after fork(). This was observed as SIGSEGV in PHP binaries that use forking when returning from the pthread_once API. Root cause: - musl uses state value 1 during pthread_once initialization - glibc uses state value (__fork_generation | 1), typically 5 - After fork(), glibc sees musl's state=1, treats it as invalid for the current fork generation, and re-initializes - This causes double initialization of pthread keys Solution: Replaced pthread_once with atomic-based initialization in AllocationTracker::get_tl_state(): - Uses std::atomic<int> with three states: kKeyNotInitialized, kKeyInitializing, kKeyInitialized - Hot path optimized with relaxed atomic load - Slow path uses compare-and-swap with proper memory ordering - Added safety timeout to prevent infinite loops - Returns nullptr on timeout
- Cover TLS init with forking
Benchmark results for collatzParameters
SummaryFound 0 performance improvements and 0 performance regressions! Performance is the same for 1 metrics, 0 unstable metrics. See unchanged results
|
Benchmark results for BadBoggleSolver_runParameters
SummaryFound 0 performance improvements and 0 performance regressions! Performance is the same for 1 metrics, 0 unstable metrics. See unchanged results
|
|
Sadly, we will have to rethink this PR. |
|
What could be happening:
A fix could be about securing the init after the fork ? |
| pthread_key_t key = _tl_state_key.load(std::memory_order_relaxed); | ||
|
|
||
| // In debug builds, verify our assumption | ||
| assert(key != kInvalidKey && "pthread key should be initialized before use"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this might trigger with a relaxed order, but at least I will know about it
I might change mem order
5dd2c62 to
dc0d1b6
Compare
- Use the key as synchronization - Avoid init during a get of the thread local state - Add a fork handler to ensure we have the key initialized
dc0d1b6 to
57ba49e
Compare
What does this PR do?
Disclaimer: I do not fully understand why we have a crash where the key was not initialized.
However we know that in the case of forks we can be in a state where we try to init in the hot path (during an allocation).
This could be the reason for the crash.
This PR addresses the symptoms.
We could go one step further here, to make sure we wrap the full init sequence (not just thread local state)
Motivation
Avoid a crash.
Additional Notes
NA
How to test the change?
I added a test for forking. Though it does not reproduce the crash.