GH-100000: Cleanup and polish various watchers code (GH-99998)

* Initialize `type_watchers` array to `NULL`s
* Optimize code watchers notification
* Optimize func watchers notification
This commit is contained in:
Itamar Ostricher 2022-12-14 11:14:16 -08:00 committed by GitHub
parent aa8591e9ca
commit ae83c78215
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 37 additions and 14 deletions

View File

@ -142,7 +142,6 @@ struct _is {
// Initialized to _PyEval_EvalFrameDefault(). // Initialized to _PyEval_EvalFrameDefault().
_PyFrameEvalFunction eval_frame; _PyFrameEvalFunction eval_frame;
PyDict_WatchCallback dict_watchers[DICT_MAX_WATCHERS];
PyFunction_WatchCallback func_watchers[FUNC_MAX_WATCHERS]; PyFunction_WatchCallback func_watchers[FUNC_MAX_WATCHERS];
// One bit is set for each non-NULL entry in func_watchers // One bit is set for each non-NULL entry in func_watchers
uint8_t active_func_watchers; uint8_t active_func_watchers;

View File

@ -630,14 +630,16 @@ static PyMethodDef test_methods[] = {
{"clear_dict_watcher", clear_dict_watcher, METH_O, NULL}, {"clear_dict_watcher", clear_dict_watcher, METH_O, NULL},
{"watch_dict", watch_dict, METH_VARARGS, NULL}, {"watch_dict", watch_dict, METH_VARARGS, NULL},
{"unwatch_dict", unwatch_dict, METH_VARARGS, NULL}, {"unwatch_dict", unwatch_dict, METH_VARARGS, NULL},
{"get_dict_watcher_events", get_dict_watcher_events, METH_NOARGS, NULL}, {"get_dict_watcher_events",
(PyCFunction) get_dict_watcher_events, METH_NOARGS, NULL},
// Type watchers. // Type watchers.
{"add_type_watcher", add_type_watcher, METH_O, NULL}, {"add_type_watcher", add_type_watcher, METH_O, NULL},
{"clear_type_watcher", clear_type_watcher, METH_O, NULL}, {"clear_type_watcher", clear_type_watcher, METH_O, NULL},
{"watch_type", watch_type, METH_VARARGS, NULL}, {"watch_type", watch_type, METH_VARARGS, NULL},
{"unwatch_type", unwatch_type, METH_VARARGS, NULL}, {"unwatch_type", unwatch_type, METH_VARARGS, NULL},
{"get_type_modified_events", get_type_modified_events, METH_NOARGS, NULL}, {"get_type_modified_events",
(PyCFunction) get_type_modified_events, METH_NOARGS, NULL},
// Code object watchers. // Code object watchers.
{"add_code_watcher", add_code_watcher, METH_O, NULL}, {"add_code_watcher", add_code_watcher, METH_O, NULL},

View File

@ -15,14 +15,21 @@ static void
notify_code_watchers(PyCodeEvent event, PyCodeObject *co) notify_code_watchers(PyCodeEvent event, PyCodeObject *co)
{ {
PyInterpreterState *interp = _PyInterpreterState_GET(); PyInterpreterState *interp = _PyInterpreterState_GET();
if (interp->active_code_watchers) { assert(interp->_initialized);
assert(interp->_initialized); uint8_t bits = interp->active_code_watchers;
for (int i = 0; i < CODE_MAX_WATCHERS; i++) { int i = 0;
while (bits) {
assert(i < CODE_MAX_WATCHERS);
if (bits & 1) {
PyCode_WatchCallback cb = interp->code_watchers[i]; PyCode_WatchCallback cb = interp->code_watchers[i];
if ((cb != NULL) && (cb(event, co) < 0)) { // callback must be non-null if the watcher bit is set
assert(cb != NULL);
if (cb(event, co) < 0) {
PyErr_WriteUnraisable((PyObject *) co); PyErr_WriteUnraisable((PyObject *) co);
} }
} }
i++;
bits >>= 1;
} }
} }

View File

@ -12,11 +12,20 @@ static void
notify_func_watchers(PyInterpreterState *interp, PyFunction_WatchEvent event, notify_func_watchers(PyInterpreterState *interp, PyFunction_WatchEvent event,
PyFunctionObject *func, PyObject *new_value) PyFunctionObject *func, PyObject *new_value)
{ {
for (int i = 0; i < FUNC_MAX_WATCHERS; i++) { uint8_t bits = interp->active_func_watchers;
PyFunction_WatchCallback cb = interp->func_watchers[i]; int i = 0;
if ((cb != NULL) && (cb(event, func, new_value) < 0)) { while (bits) {
PyErr_WriteUnraisable((PyObject *) func); assert(i < FUNC_MAX_WATCHERS);
if (bits & 1) {
PyFunction_WatchCallback cb = interp->func_watchers[i];
// callback must be non-null if the watcher bit is set
assert(cb != NULL);
if (cb(event, func, new_value) < 0) {
PyErr_WriteUnraisable((PyObject *) func);
}
} }
i++;
bits >>= 1;
} }
} }
@ -25,6 +34,7 @@ handle_func_event(PyFunction_WatchEvent event, PyFunctionObject *func,
PyObject *new_value) PyObject *new_value)
{ {
PyInterpreterState *interp = _PyInterpreterState_GET(); PyInterpreterState *interp = _PyInterpreterState_GET();
assert(interp->_initialized);
if (interp->active_func_watchers) { if (interp->active_func_watchers) {
notify_func_watchers(interp, event, func, new_value); notify_func_watchers(interp, event, func, new_value);
} }

View File

@ -485,23 +485,24 @@ PyType_Modified(PyTypeObject *type)
} }
} }
// Notify registered type watchers, if any
if (type->tp_watched) { if (type->tp_watched) {
PyInterpreterState *interp = _PyInterpreterState_GET(); PyInterpreterState *interp = _PyInterpreterState_GET();
int bits = type->tp_watched; int bits = type->tp_watched;
int i = 0; int i = 0;
while(bits && i < TYPE_MAX_WATCHERS) { while (bits) {
assert(i < TYPE_MAX_WATCHERS);
if (bits & 1) { if (bits & 1) {
PyType_WatchCallback cb = interp->type_watchers[i]; PyType_WatchCallback cb = interp->type_watchers[i];
if (cb && (cb(type) < 0)) { if (cb && (cb(type) < 0)) {
PyErr_WriteUnraisable((PyObject *)type); PyErr_WriteUnraisable((PyObject *)type);
} }
} }
i += 1; i++;
bits >>= 1; bits >>= 1;
} }
} }
type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG; type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG;
type->tp_version_tag = 0; /* 0 is not a valid version tag */ type->tp_version_tag = 0; /* 0 is not a valid version tag */
} }

View File

@ -461,6 +461,10 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
interp->dict_state.watchers[i] = NULL; interp->dict_state.watchers[i] = NULL;
} }
for (int i=0; i < TYPE_MAX_WATCHERS; i++) {
interp->type_watchers[i] = NULL;
}
for (int i=0; i < FUNC_MAX_WATCHERS; i++) { for (int i=0; i < FUNC_MAX_WATCHERS; i++) {
interp->func_watchers[i] = NULL; interp->func_watchers[i] = NULL;
} }