GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — gh-117657: Quiet more TSAN warnings due to incorrect modeling of compare/exchange by mpage · Pull Request #117830 · python/cpython Conversation
|
| static void | |
| detach_thread(PyThreadState *tstate, int detached_state) | |
| { | |
| // XXX assert(tstate_is_alive(tstate) && tstate_is_bound(tstate)); | |
| assert(tstate->state == _Py_THREAD_ATTACHED); | |
| assert(tstate == current_fast_get()); | |
| if (tstate->critical_section != 0) { | |
| _PyCriticalSection_SuspendAll(tstate); | |
| } | |
| #ifdef Py_GIL_DISABLED | |
| _Py_qsbr_detach(((_PyThreadStateImpl *)tstate)->qsbr); | |
| #endif | |
| tstate_deactivate(tstate); | |
| tstate_set_detached(tstate, detached_state); |
Lines 2003 to 2008 in eca5362
| static void | |
| tstate_set_detached(PyThreadState *tstate, int detached_state) | |
| { | |
| assert(tstate->state == _Py_THREAD_ATTACHED); | |
| #ifdef Py_GIL_DISABLED | |
| _Py_atomic_store_int(&tstate->state, detached_state); |
and the atomic compare/exchange of tstate->state in park_detached_threads():
Lines 2159 to 2170 in eca5362
| static bool | |
| park_detached_threads(struct _stoptheworld_state *stw) | |
| { | |
| int num_parked = 0; | |
| PyInterpreterState *i; | |
| PyThreadState *t; | |
| _Py_FOR_EACH_THREAD(stw, i, t) { | |
| int state = _Py_atomic_load_int_relaxed(&t->state); | |
| if (state == _Py_THREAD_DETACHED) { | |
| // Atomically transition to "suspended" if in "detached" state. | |
| if (_Py_atomic_compare_exchange_int(&t->state, | |
| &state, _Py_THREAD_SUSPENDED)) { |
I believe this is due to a bug with how TSAN models compare/exchange. Note that this appears to be a different issue than what we're seeing in #117828; the compare/exhange is succeeding in this case.
According to the standard,
If one evaluation modifies a memory location, and the other reads or modifies the same memory location, and if at least one of the evaluations is not an atomic operation, the behavior of the program is undefined (the program has a data race) unless there exists a happens-before relationship between these two evaluations.
However, according to the standard, A strongly happens-before B if any of the following are true:
- A is sequenced-before B.
- A synchronizes-with B.
- A strongly happens-before X, and X strongly happens-before B.
Using this we can establish a happens-before relationship between each non-atomic load and the compare/exchange:
tstate->state that happens in tstate_set_detached(). Therefore, each non-atomic load happens-before the seq-cst store.tstate->state in tstate_set_detached() synchronizes-with the compare/exchange to tstate->state in park_detached_threads(). Thus the seq-cst store happens-before the compare/exchange.tstate->state, therefore they should not be flagged as a data races.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
TSAN complains about data races between the non-atomic load of `tstate->state` in `detach_thread()` and `tstate_set_detached()` and the atomic compare/exchange of `tstate->tstate` in `park_detached_threads()`. I believe this is due to a bug with how TSAN models compare/exchange (treating it solely as a store, rather than a load/store). According to the standard, > If one evaluation modifies a memory location, and the other > reads or modifies the same memory location, and if at least one > of the evaluations is not an atomic operation, the behavior of > the program is undefined (the program has a data race) unless > there exists a happens-before relationship between these two > evaluations. However, according to the standard, A happens-before B if any of the following are true: > 1) A is sequenced-before B. > 2) A synchronizes-with B. > 3) A strongly happens-before X, and X strongly happens-before B. Using this we can establish a happens-before relationship between each non-atomic load and the compare/exchange: 1. Each non-atomic load is sequenced-before the seq-cst store to `tstate->state` that happens in `tstate_set_detached()`. Therefore, each non-atomic load happens-before the seq-cst store. 2. The seq-cst store to `tstate->state` in `tstate_set_detached()` synchronizes-with the compare/exchange to `tstate->state` in `park_detached_threads()`. Thus the seq-cst store happens-before the compare/exchange. 3. (1) and (2) establish the happens-before relationship between the non-atomic load and the compare/exchange on `tstate->state`, therefore it should not be flagged as a data race.
The underlying races were fixed in pythongh-117830 and pythongh-117828
…f compare/exchange (python#117830)
…tate` These should be the last instances of TSAN warning of data races when accessing `tstate->state` non-atomically. TSAN reports a race between accessing `tstate->state` in `_PyThreadState_Suspend()` and the compare/exhange in `park_detached_threads`. This is a false positive due to TSAN modeling failed compare/exchange operations as writes. TSAN reports a race between accessing `tstate->state` in `_PySemaphore_Wait()` and the compare/exchange in `park_detached_threads`. This is the same issue that we saw in pythongh-117830.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.