Repository navigation
Conversation
Documentation build overview
|
|
@cobed95 Sorry for the delay, I will take a look at this week. |
|
Yeah I will do this during this sprint |
| // lets two threads select the same slot and return the same ID. | ||
| // Losing the race means the slot is taken, so just try the next one. | ||
| PyContext_WatchCallback expected = NULL; | ||
| if (_Py_atomic_compare_exchange_ptr(&interp->context_watchers[i], |
There was a problem hiding this comment.
It doesn not gurentee to set set_context_watcher_bit if CAS is not success, you may run loop here.
| clear_context_watcher_bit(interp, watcher_id); | ||
| FT_ATOMIC_STORE_PTR_RELEASE(interp->context_watchers[watcher_id], NULL); |
There was a problem hiding this comment.
This will make issue if one thread clear watcher_id and other thread try to add watcher_id.
Seqeuntial operation should be gurenteed.
| // Stop notification selecting this slot before retiring the callback, so | ||
| // the window in which a notifier sees the bit but loads NULL is as short | ||
| // as possible. That window is benign: notify_context_watchers() skips a | ||
| // NULL callback. |
There was a problem hiding this comment.
Please rewrite comment as concise as possible.
corona10
left a comment
There was a problem hiding this comment.
I thint that following functions should use critical section to gurentee sequetiallly consistant output.
- PyContext_ClearWatcher
- PyContext_AddWatcher
|
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
#155619
Reproduction
gh-155619 was reported from source inspection without a reproducer, so here is one first. The new test registers a watcher whose callback does nothing, then races context switches against add/clear. On unpatched main it aborts every time:
Same binary, only the GIL toggled.
There are three races.
PyContext_AddWatcher()claimed a slot with a plain test-then-store, so two threads could take the same slot and get the same watcher ID. It then stored the callback and set the active bit as ordinary writes, so a notifier on a weakly ordered machine could see the bit before the pointer. Andnotify_context_watchers()reads the bitmask and then loads the slot, whichPyContext_ClearWatcher()can empty in between — that one is a plain interleaving, so no amount of memory ordering fixes it.Fix
The fix claims slots with
_Py_atomic_compare_exchange_ptr(), publishes the callback with a release store paired with an acquire load in notification, and skips a NULL callback instead of asserting on it, same as_PyDict_SendEvent(). No locks: notification runs on every context switch and does no atomic read-modify-write there. I kept the bitmask rather than scanning all eight slots because it makes the common zero-watcher case one load and a not-taken branch.Docs
Doc/c-api/contextvars.rsthad no threading guidance at all, unlike dict and type watchers, so I added some.Testing
The new test passes 20/20 runs;
test_free_threading,test_context,test_capi,test_contextlibandtest_asyncioare clean on a free-threaded build (4735 tests), and the same binary withPYTHON_GIL=1is clean too.Benchmarks
pyperformance
async_tree,async_tree_io,async_generators(asyncio runs each task step throughctx.run()), plusnbodyas a control that never enters a context. Free-threaded build, baseline vs. this change:The default build was the same story, except
async_generatorscame out 1.08x slower there. I think that one is noise: its spread is about 12% of the mean, t=-2.54, and the same benchmark went faster on the free-threaded build. These were plain-O3builds with--faston a laptop, so they answer "does this cost anything" and not much else; happy to redo them properly if it matters.No ThreadSanitizer run from me, though
test_free_threadingis inTSAN_TESTS, so CI should cover the new test.Approach discussed with @corona10 at the PyCon KR sprint.