Skip to content

Commit ae83c78

Browse files
authored
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
1 parent aa8591e commit ae83c78

File tree

6 files changed

+37
-14
lines changed

6 files changed

+37
-14
lines changed

Include/internal/pycore_interp.h

-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ struct _is {
142142
// Initialized to _PyEval_EvalFrameDefault().
143143
_PyFrameEvalFunction eval_frame;
144144

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

Modules/_testcapi/watchers.c

+4-2
Original file line numberDiff line numberDiff line change
@@ -630,14 +630,16 @@ static PyMethodDef test_methods[] = {
630630
{"clear_dict_watcher", clear_dict_watcher, METH_O, NULL},
631631
{"watch_dict", watch_dict, METH_VARARGS, NULL},
632632
{"unwatch_dict", unwatch_dict, METH_VARARGS, NULL},
633-
{"get_dict_watcher_events", get_dict_watcher_events, METH_NOARGS, NULL},
633+
{"get_dict_watcher_events",
634+
(PyCFunction) get_dict_watcher_events, METH_NOARGS, NULL},
634635

635636
// Type watchers.
636637
{"add_type_watcher", add_type_watcher, METH_O, NULL},
637638
{"clear_type_watcher", clear_type_watcher, METH_O, NULL},
638639
{"watch_type", watch_type, METH_VARARGS, NULL},
639640
{"unwatch_type", unwatch_type, METH_VARARGS, NULL},
640-
{"get_type_modified_events", get_type_modified_events, METH_NOARGS, NULL},
641+
{"get_type_modified_events",
642+
(PyCFunction) get_type_modified_events, METH_NOARGS, NULL},
641643

642644
// Code object watchers.
643645
{"add_code_watcher", add_code_watcher, METH_O, NULL},

Objects/codeobject.c

+11-4
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,21 @@ static void
1515
notify_code_watchers(PyCodeEvent event, PyCodeObject *co)
1616
{
1717
PyInterpreterState *interp = _PyInterpreterState_GET();
18-
if (interp->active_code_watchers) {
19-
assert(interp->_initialized);
20-
for (int i = 0; i < CODE_MAX_WATCHERS; i++) {
18+
assert(interp->_initialized);
19+
uint8_t bits = interp->active_code_watchers;
20+
int i = 0;
21+
while (bits) {
22+
assert(i < CODE_MAX_WATCHERS);
23+
if (bits & 1) {
2124
PyCode_WatchCallback cb = interp->code_watchers[i];
22-
if ((cb != NULL) && (cb(event, co) < 0)) {
25+
// callback must be non-null if the watcher bit is set
26+
assert(cb != NULL);
27+
if (cb(event, co) < 0) {
2328
PyErr_WriteUnraisable((PyObject *) co);
2429
}
2530
}
31+
i++;
32+
bits >>= 1;
2633
}
2734
}
2835

Objects/funcobject.c

+14-4
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,20 @@ static void
1212
notify_func_watchers(PyInterpreterState *interp, PyFunction_WatchEvent event,
1313
PyFunctionObject *func, PyObject *new_value)
1414
{
15-
for (int i = 0; i < FUNC_MAX_WATCHERS; i++) {
16-
PyFunction_WatchCallback cb = interp->func_watchers[i];
17-
if ((cb != NULL) && (cb(event, func, new_value) < 0)) {
18-
PyErr_WriteUnraisable((PyObject *) func);
15+
uint8_t bits = interp->active_func_watchers;
16+
int i = 0;
17+
while (bits) {
18+
assert(i < FUNC_MAX_WATCHERS);
19+
if (bits & 1) {
20+
PyFunction_WatchCallback cb = interp->func_watchers[i];
21+
// callback must be non-null if the watcher bit is set
22+
assert(cb != NULL);
23+
if (cb(event, func, new_value) < 0) {
24+
PyErr_WriteUnraisable((PyObject *) func);
25+
}
1926
}
27+
i++;
28+
bits >>= 1;
2029
}
2130
}
2231

@@ -25,6 +34,7 @@ handle_func_event(PyFunction_WatchEvent event, PyFunctionObject *func,
2534
PyObject *new_value)
2635
{
2736
PyInterpreterState *interp = _PyInterpreterState_GET();
37+
assert(interp->_initialized);
2838
if (interp->active_func_watchers) {
2939
notify_func_watchers(interp, event, func, new_value);
3040
}

Objects/typeobject.c

+4-3
Original file line numberDiff line numberDiff line change
@@ -485,23 +485,24 @@ PyType_Modified(PyTypeObject *type)
485485
}
486486
}
487487

488+
// Notify registered type watchers, if any
488489
if (type->tp_watched) {
489490
PyInterpreterState *interp = _PyInterpreterState_GET();
490491
int bits = type->tp_watched;
491492
int i = 0;
492-
while(bits && i < TYPE_MAX_WATCHERS) {
493+
while (bits) {
494+
assert(i < TYPE_MAX_WATCHERS);
493495
if (bits & 1) {
494496
PyType_WatchCallback cb = interp->type_watchers[i];
495497
if (cb && (cb(type) < 0)) {
496498
PyErr_WriteUnraisable((PyObject *)type);
497499
}
498500
}
499-
i += 1;
501+
i++;
500502
bits >>= 1;
501503
}
502504
}
503505

504-
505506
type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG;
506507
type->tp_version_tag = 0; /* 0 is not a valid version tag */
507508
}

Python/pystate.c

+4
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,10 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
461461
interp->dict_state.watchers[i] = NULL;
462462
}
463463

464+
for (int i=0; i < TYPE_MAX_WATCHERS; i++) {
465+
interp->type_watchers[i] = NULL;
466+
}
467+
464468
for (int i=0; i < FUNC_MAX_WATCHERS; i++) {
465469
interp->func_watchers[i] = NULL;
466470
}

0 commit comments

Comments
 (0)