Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add a bit vector to optimize watcher dispatch
A bit is set in the bit vector iff there is a watcher set at the
corresponding offset in the watcher array. Only notify watchers
if at least one bit is set.
  • Loading branch information
mpage committed Nov 22, 2022
commit 812dd5fa42ad2f3d6820838a4e18ea9dfc94bff2
2 changes: 2 additions & 0 deletions Include/internal/pycore_interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ struct _is {

PyDict_WatchCallback dict_watchers[DICT_MAX_WATCHERS];
PyFunction_WatchCallback func_watchers[FUNC_MAX_WATCHERS];
// One bit is set for each non-NULL entry in func_watchers
uint8_t active_func_watchers;

Py_ssize_t co_extra_user_count;
freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS];
Expand Down
16 changes: 14 additions & 2 deletions Objects/funcobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
#include "structmember.h" // PyMemberDef

static void
handle_func_event(PyFunction_WatchEvent event, PyFunctionObject *func, PyObject *new_value)
notify_func_watchers(PyInterpreterState *interp, PyFunction_WatchEvent event,
PyFunctionObject *func, PyObject *new_value)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
for (int i = 0; i < FUNC_MAX_WATCHERS; i++) {
PyFunction_WatchCallback cb = interp->func_watchers[i];
if ((cb != NULL) && (cb(event, func, new_value) < 0)) {
Expand All @@ -20,6 +20,16 @@ handle_func_event(PyFunction_WatchEvent event, PyFunctionObject *func, PyObject
}
}

static inline void
handle_func_event(PyFunction_WatchEvent event, PyFunctionObject *func,
PyObject *new_value)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
if (interp->active_func_watchers) {
notify_func_watchers(interp, event, func, new_value);
}
}

int
PyFunction_AddWatcher(PyFunction_WatchCallback callback)
{
Expand All @@ -28,6 +38,7 @@ PyFunction_AddWatcher(PyFunction_WatchCallback callback)
for (int i = 0; i < FUNC_MAX_WATCHERS; i++) {
if (interp->func_watchers[i] == NULL) {
interp->func_watchers[i] = callback;
interp->active_func_watchers |= (1 << i);
return i;
}
}
Expand All @@ -50,6 +61,7 @@ PyFunction_ClearWatcher(int watcher_id)
return -1;
}
interp->func_watchers[watcher_id] = NULL;
interp->active_func_watchers &= ~(1 << watcher_id);
return 0;
}

Expand Down
1 change: 1 addition & 0 deletions Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
for (int i=0; i < FUNC_MAX_WATCHERS; i++) {
interp->func_watchers[i] = NULL;
}
interp->active_func_watchers = 0;

// XXX Once we have one allocator per interpreter (i.e.
// per-interpreter GC) we must ensure that all of the interpreter's
Expand Down