mirror of https://github.com/python/cpython
gh-118789: Add `PyUnstable_Object_ClearWeakRefsNoCallbacks` (#118807)
This exposes `PyUnstable_Object_ClearWeakRefsNoCallbacks` as an unstable C-API function to provide a thread-safe mechanism for clearing weakrefs without executing callbacks. Some C-API extensions need to clear weakrefs without calling callbacks, such as after running finalizers like we do in subtype_dealloc. Previously they could use `_PyWeakref_ClearRef` on each weakref, but that's not thread-safe in the free-threaded build. Co-authored-by: Petr Viktorin <encukou@gmail.com>
This commit is contained in:
parent
360f14a493
commit
e8752d7b80
|
@ -96,3 +96,19 @@ as much as it can.
|
||||||
This iterates through the weak references for *object* and calls callbacks
|
This iterates through the weak references for *object* and calls callbacks
|
||||||
for those references which have one. It returns when all callbacks have
|
for those references which have one. It returns when all callbacks have
|
||||||
been attempted.
|
been attempted.
|
||||||
|
|
||||||
|
|
||||||
|
.. c:function:: void PyUnstable_Object_ClearWeakRefsNoCallbacks(PyObject *object)
|
||||||
|
|
||||||
|
Clears the weakrefs for *object* without calling the callbacks.
|
||||||
|
|
||||||
|
This function is called by the :c:member:`~PyTypeObject.tp_dealloc` handler
|
||||||
|
for types with finalizers (i.e., :meth:`~object.__del__`). The handler for
|
||||||
|
those objects first calls :c:func:`PyObject_ClearWeakRefs` to clear weakrefs
|
||||||
|
and call their callbacks, then the finalizer, and finally this function to
|
||||||
|
clear any weakrefs that may have been created by the finalizer.
|
||||||
|
|
||||||
|
In most circumstances, it's more appropriate to use
|
||||||
|
:c:func:`PyObject_ClearWeakRefs` to clear weakrefs instead of this function.
|
||||||
|
|
||||||
|
.. versionadded:: 3.13
|
||||||
|
|
|
@ -288,6 +288,8 @@ PyAPI_FUNC(PyObject **) _PyObject_GetDictPtr(PyObject *);
|
||||||
PyAPI_FUNC(void) PyObject_CallFinalizer(PyObject *);
|
PyAPI_FUNC(void) PyObject_CallFinalizer(PyObject *);
|
||||||
PyAPI_FUNC(int) PyObject_CallFinalizerFromDealloc(PyObject *);
|
PyAPI_FUNC(int) PyObject_CallFinalizerFromDealloc(PyObject *);
|
||||||
|
|
||||||
|
PyAPI_FUNC(void) PyUnstable_Object_ClearWeakRefsNoCallbacks(PyObject *);
|
||||||
|
|
||||||
/* Same as PyObject_Generic{Get,Set}Attr, but passing the attributes
|
/* Same as PyObject_Generic{Get,Set}Attr, but passing the attributes
|
||||||
dict as the last parameter. */
|
dict as the last parameter. */
|
||||||
PyAPI_FUNC(PyObject *)
|
PyAPI_FUNC(PyObject *)
|
||||||
|
|
|
@ -109,7 +109,7 @@ extern Py_ssize_t _PyWeakref_GetWeakrefCount(PyObject *obj);
|
||||||
|
|
||||||
// Clear all the weak references to obj but leave their callbacks uncalled and
|
// Clear all the weak references to obj but leave their callbacks uncalled and
|
||||||
// intact.
|
// intact.
|
||||||
extern void _PyWeakref_ClearWeakRefsExceptCallbacks(PyObject *obj);
|
extern void _PyWeakref_ClearWeakRefsNoCallbacks(PyObject *obj);
|
||||||
|
|
||||||
PyAPI_FUNC(int) _PyWeakref_IsDead(PyObject *weakref);
|
PyAPI_FUNC(int) _PyWeakref_IsDead(PyObject *weakref);
|
||||||
|
|
||||||
|
|
|
@ -103,5 +103,33 @@ class PrintTest(unittest.TestCase):
|
||||||
with self.assertRaises(OSError):
|
with self.assertRaises(OSError):
|
||||||
_testcapi.pyobject_print_os_error(output_filename)
|
_testcapi.pyobject_print_os_error(output_filename)
|
||||||
|
|
||||||
|
|
||||||
|
class ClearWeakRefsNoCallbacksTest(unittest.TestCase):
|
||||||
|
"""Test PyUnstable_Object_ClearWeakRefsNoCallbacks"""
|
||||||
|
def test_ClearWeakRefsNoCallbacks(self):
|
||||||
|
"""Ensure PyUnstable_Object_ClearWeakRefsNoCallbacks works"""
|
||||||
|
import weakref
|
||||||
|
import gc
|
||||||
|
class C:
|
||||||
|
pass
|
||||||
|
obj = C()
|
||||||
|
messages = []
|
||||||
|
ref = weakref.ref(obj, lambda: messages.append("don't add this"))
|
||||||
|
self.assertIs(ref(), obj)
|
||||||
|
self.assertFalse(messages)
|
||||||
|
_testcapi.pyobject_clear_weakrefs_no_callbacks(obj)
|
||||||
|
self.assertIsNone(ref())
|
||||||
|
gc.collect()
|
||||||
|
self.assertFalse(messages)
|
||||||
|
|
||||||
|
def test_ClearWeakRefsNoCallbacks_no_weakref_support(self):
|
||||||
|
"""Don't fail on objects that don't support weakrefs"""
|
||||||
|
import weakref
|
||||||
|
obj = object()
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
ref = weakref.ref(obj)
|
||||||
|
_testcapi.pyobject_clear_weakrefs_no_callbacks(obj)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Add :c:func:`PyUnstable_Object_ClearWeakRefsNoCallbacks`, which clears
|
||||||
|
weakrefs without calling their callbacks.
|
|
@ -117,11 +117,19 @@ pyobject_print_os_error(PyObject *self, PyObject *args)
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
pyobject_clear_weakrefs_no_callbacks(PyObject *self, PyObject *obj)
|
||||||
|
{
|
||||||
|
PyUnstable_Object_ClearWeakRefsNoCallbacks(obj);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
static PyMethodDef test_methods[] = {
|
static PyMethodDef test_methods[] = {
|
||||||
{"call_pyobject_print", call_pyobject_print, METH_VARARGS},
|
{"call_pyobject_print", call_pyobject_print, METH_VARARGS},
|
||||||
{"pyobject_print_null", pyobject_print_null, METH_VARARGS},
|
{"pyobject_print_null", pyobject_print_null, METH_VARARGS},
|
||||||
{"pyobject_print_noref_object", pyobject_print_noref_object, METH_VARARGS},
|
{"pyobject_print_noref_object", pyobject_print_noref_object, METH_VARARGS},
|
||||||
{"pyobject_print_os_error", pyobject_print_os_error, METH_VARARGS},
|
{"pyobject_print_os_error", pyobject_print_os_error, METH_VARARGS},
|
||||||
|
{"pyobject_clear_weakrefs_no_callbacks", pyobject_clear_weakrefs_no_callbacks, METH_O},
|
||||||
|
|
||||||
{NULL},
|
{NULL},
|
||||||
};
|
};
|
||||||
|
|
|
@ -2533,7 +2533,7 @@ subtype_dealloc(PyObject *self)
|
||||||
finalizers since they might rely on part of the object
|
finalizers since they might rely on part of the object
|
||||||
being finalized that has already been destroyed. */
|
being finalized that has already been destroyed. */
|
||||||
if (type->tp_weaklistoffset && !base->tp_weaklistoffset) {
|
if (type->tp_weaklistoffset && !base->tp_weaklistoffset) {
|
||||||
_PyWeakref_ClearWeakRefsExceptCallbacks(self);
|
_PyWeakref_ClearWeakRefsNoCallbacks(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1016,7 +1016,7 @@ PyObject_ClearWeakRefs(PyObject *object)
|
||||||
PyObject *exc = PyErr_GetRaisedException();
|
PyObject *exc = PyErr_GetRaisedException();
|
||||||
PyObject *tuple = PyTuple_New(num_weakrefs * 2);
|
PyObject *tuple = PyTuple_New(num_weakrefs * 2);
|
||||||
if (tuple == NULL) {
|
if (tuple == NULL) {
|
||||||
_PyWeakref_ClearWeakRefsExceptCallbacks(object);
|
_PyWeakref_ClearWeakRefsNoCallbacks(object);
|
||||||
PyErr_WriteUnraisable(NULL);
|
PyErr_WriteUnraisable(NULL);
|
||||||
PyErr_SetRaisedException(exc);
|
PyErr_SetRaisedException(exc);
|
||||||
return;
|
return;
|
||||||
|
@ -1057,6 +1057,14 @@ PyObject_ClearWeakRefs(PyObject *object)
|
||||||
PyErr_SetRaisedException(exc);
|
PyErr_SetRaisedException(exc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PyUnstable_Object_ClearWeakRefsNoCallbacks(PyObject *obj)
|
||||||
|
{
|
||||||
|
if (_PyType_SUPPORTS_WEAKREFS(Py_TYPE(obj))) {
|
||||||
|
_PyWeakref_ClearWeakRefsNoCallbacks(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* This function is called by _PyStaticType_Dealloc() to clear weak references.
|
/* This function is called by _PyStaticType_Dealloc() to clear weak references.
|
||||||
*
|
*
|
||||||
* This is called at the end of runtime finalization, so we can just
|
* This is called at the end of runtime finalization, so we can just
|
||||||
|
@ -1076,7 +1084,7 @@ _PyStaticType_ClearWeakRefs(PyInterpreterState *interp, PyTypeObject *type)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
_PyWeakref_ClearWeakRefsExceptCallbacks(PyObject *obj)
|
_PyWeakref_ClearWeakRefsNoCallbacks(PyObject *obj)
|
||||||
{
|
{
|
||||||
/* Modeled after GET_WEAKREFS_LISTPTR().
|
/* Modeled after GET_WEAKREFS_LISTPTR().
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue