-
-
Notifications
You must be signed in to change notification settings - Fork 32.2k
gh-102381: don't call watcher callback with dead object #102382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
6d0b789
1ccec46
8e8843e
b5256e7
9b938c7
6b323fa
a31af32
b893187
4894127
33dda2a
239981c
e391218
05e2ce0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,8 @@ | |
#include "pycore_pyerrors.h" // _PyErr_Occurred() | ||
#include "structmember.h" // PyMemberDef | ||
|
||
static PyObject* func_repr(PyFunctionObject *op); | ||
|
||
static void | ||
notify_func_watchers(PyInterpreterState *interp, PyFunction_WatchEvent event, | ||
PyFunctionObject *func, PyObject *new_value) | ||
|
@@ -21,7 +23,13 @@ notify_func_watchers(PyInterpreterState *interp, PyFunction_WatchEvent event, | |
// 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); | ||
// Don't risk resurrecting the func if an unraisablehook keeps a | ||
// reference; pass a string as context. | ||
PyObject *repr = func_repr(func); | ||
PyObject *context = PyUnicode_FromFormat("watcher callback for %U", repr); | ||
PyErr_WriteUnraisable(context); | ||
Py_DECREF(context); | ||
Py_DECREF(repr); | ||
} | ||
} | ||
i++; | ||
|
@@ -33,6 +41,7 @@ static inline void | |
handle_func_event(PyFunction_WatchEvent event, PyFunctionObject *func, | ||
PyObject *new_value) | ||
{ | ||
assert(Py_REFCNT(func) > 0); | ||
PyInterpreterState *interp = _PyInterpreterState_GET(); | ||
assert(interp->_initialized); | ||
if (interp->active_func_watchers) { | ||
|
@@ -766,7 +775,14 @@ func_clear(PyFunctionObject *op) | |
static void | ||
func_dealloc(PyFunctionObject *op) | ||
{ | ||
assert(Py_REFCNT(op) == 0); | ||
Py_SET_REFCNT(op, 1); | ||
handle_func_event(PyFunction_EVENT_DESTROY, op, NULL); | ||
if (Py_REFCNT(op) > 1) { | ||
Py_DECREF(op); | ||
carljm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return; | ||
} | ||
Py_SET_REFCNT(op, 0); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the normal case, assuming the watcher has not taken any new reference to the object, the refcount will be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The watcher must take a reference to any object it is watching, otherwise the refcount is incorrect and we crash and burn. The GC, or an optimizer, can legitimately assume that any object with a reference count of zero and no weakrefs is to be freed and re-use the memory. That's not going to end well if the watcher still thinks that memory is still a dictionary. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @markshannon I think your comment is more relevant to the discussion below (about why to have dealloc watchers at all) than it is to this thread, so I'll reply below. The answer to @kumaraditya303's question here is that this |
||
_PyObject_GC_UNTRACK(op); | ||
if (op->func_weakreflist != NULL) { | ||
PyObject_ClearWeakRefs((PyObject *) op); | ||
|
Uh oh!
There was an error while loading. Please reload this page.