mirror of
https://github.com/python/cpython.git
synced 2026-06-05 17:31:21 +00:00
gh-145235: Make dict watcher API thread-safe for free-threaded builds (gh-145233)
In free-threaded builds, concurrent calls to PyDict_AddWatcher, PyDict_ClearWatcher, PyDict_Watch, and PyDict_Unwatch can race on the shared callback array and the per-dict watcher tags. This change adds a mutex to serialize watcher registration and removal, atomic operations for tag updates, and atomic acquire/release synchronization for callback dispatch in _PyDict_SendEvent.
This commit is contained in:
parent
fadd9bc14e
commit
8a4895985f
9 changed files with 159 additions and 27 deletions
|
|
@ -18,6 +18,7 @@
|
|||
#include "pycore_opcode_metadata.h"
|
||||
#include "pycore_opcode_utils.h"
|
||||
#include "pycore_pystate.h" // _PyInterpreterState_GET()
|
||||
#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_*
|
||||
#include "pycore_tstate.h" // _PyThreadStateImpl
|
||||
#include "pycore_uop_metadata.h"
|
||||
#include "pycore_long.h"
|
||||
|
|
@ -127,7 +128,7 @@ static void
|
|||
increment_mutations(PyObject* dict) {
|
||||
assert(PyDict_CheckExact(dict));
|
||||
PyDictObject *d = (PyDictObject *)dict;
|
||||
FT_ATOMIC_ADD_UINT64(d->_ma_watcher_tag, (1 << DICT_MAX_WATCHERS));
|
||||
FT_ATOMIC_ADD_UINT64(d->_ma_watcher_tag, 1ULL << DICT_MAX_WATCHERS);
|
||||
}
|
||||
|
||||
/* The first two dict watcher IDs are reserved for CPython,
|
||||
|
|
@ -156,6 +157,17 @@ type_watcher_callback(PyTypeObject* type)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
_setup_optimizer_watchers(void *Py_UNUSED(arg))
|
||||
{
|
||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||
FT_ATOMIC_STORE_PTR_RELEASE(
|
||||
interp->dict_state.watchers[GLOBALS_WATCHER_ID],
|
||||
globals_watcher_callback);
|
||||
interp->type_watchers[TYPE_WATCHER_ID] = type_watcher_callback;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
watch_type(PyTypeObject *type, _PyBloomFilter *filter)
|
||||
{
|
||||
|
|
@ -580,10 +592,8 @@ optimize_uops(
|
|||
|
||||
// Make sure that watchers are set up
|
||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||
if (interp->dict_state.watchers[GLOBALS_WATCHER_ID] == NULL) {
|
||||
interp->dict_state.watchers[GLOBALS_WATCHER_ID] = globals_watcher_callback;
|
||||
interp->type_watchers[TYPE_WATCHER_ID] = type_watcher_callback;
|
||||
}
|
||||
_PyOnceFlag_CallOnce(&interp->dict_state.watcher_setup_once,
|
||||
_setup_optimizer_watchers, NULL);
|
||||
|
||||
_Py_uop_abstractcontext_init(ctx, dependencies);
|
||||
_Py_UOpsAbstractFrame *frame = _Py_uop_frame_new(ctx, (PyCodeObject *)func->func_code, NULL, 0);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue