Bug report
Currently, there is a data race when PyDict_Watch/PyDict_Unwatch is called on a dict that another thread may concurrently mutate: the read of _ma_watcher_tag in _PyDict_NotifyEvent (on the mutation path) races with the modification by PyDict_Watch/PyDict_Unwatch. Additionally, concurrent PyDict_Watch/PyDict_Unwatch calls on the same dict (e.g. from different watcher IDs) race with each other, since both are non-atomic read-modify-write operations on _ma_watcher_tag.
In the free threading build, we should use atomic operations for the read of _ma_watcher_tag in _PyDict_NotifyEvent and for the updates performed by PyDict_Watch/PyDict_Unwatch.
Linked PRs
- gh-148393: Use atomic ops on _ma_watcher_tag in free threading build #148397
- [3.14] gh-148393: Use atomic ops on _ma_watcher_tag in free threading build (gh-148397) #148451
- gh-148393: Use acquire load for _ma_watcher_tag in dict notify event #148509
- [3.14] gh-148393: Use acquire load for _ma_watcher_tag in dict notify event (gh-148509) #148512