]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-128008: Add `PyWeakref_IsDead()` (GH-128009)
authorSam Gross <colesbury@gmail.com>
Thu, 19 Dec 2024 15:17:15 +0000 (10:17 -0500)
committerGitHub <noreply@github.com>
Thu, 19 Dec 2024 15:17:15 +0000 (16:17 +0100)
The `PyWeakref_IsDead()` function tests if a weak reference is dead
without any side effects. Although you can also detect if a weak
reference is dead using `PyWeakref_GetRef()`, that function returns a
strong reference that must be `Py_DECREF()`'d, which can introduce side
effects if the last reference is concurrently dropped (at least in the
free threading build).

Doc/c-api/weakref.rst
Include/cpython/weakrefobject.h
Misc/NEWS.d/next/C_API/2024-12-16-21-59-06.gh-issue-128008.fa9Jt0.rst [new file with mode: 0644]
Modules/_testcapimodule.c
Objects/weakrefobject.c

index 8f233e16fb17cfa4994a5f14fe5d4f183cdbd4c1..c3c6cf413dcef586888ff51d7b0cd432fbe547fb 100644 (file)
@@ -88,6 +88,15 @@ as much as it can.
       Use :c:func:`PyWeakref_GetRef` instead.
 
 
+.. c:function:: int PyWeakref_IsDead(PyObject *ref)
+
+   Test if the weak reference *ref* is dead. Returns 1 if the reference is
+   dead, 0 if it is alive, and -1 with an error set if *ref* is not a weak
+   reference object.
+
+   .. versionadded:: 3.14
+
+
 .. c:function:: void PyObject_ClearWeakRefs(PyObject *object)
 
    This function is called by the :c:member:`~PyTypeObject.tp_dealloc` handler
index 9aa1a92c413fe9bd81070b8356f42dd00bafcd09..da8e77cddaca63823c65e577b0b23f664bf4a32a 100644 (file)
@@ -45,6 +45,9 @@ PyAPI_FUNC(void) _PyWeakref_ClearRef(PyWeakReference *self);
 #define _PyWeakref_CAST(op) \
     (assert(PyWeakref_Check(op)), _Py_CAST(PyWeakReference*, (op)))
 
+// Test if a weak reference is dead.
+PyAPI_FUNC(int) PyWeakref_IsDead(PyObject *ref);
+
 Py_DEPRECATED(3.13) static inline PyObject* PyWeakref_GET_OBJECT(PyObject *ref_obj)
 {
     PyWeakReference *ref = _PyWeakref_CAST(ref_obj);
diff --git a/Misc/NEWS.d/next/C_API/2024-12-16-21-59-06.gh-issue-128008.fa9Jt0.rst b/Misc/NEWS.d/next/C_API/2024-12-16-21-59-06.gh-issue-128008.fa9Jt0.rst
new file mode 100644 (file)
index 0000000..2349ecc
--- /dev/null
@@ -0,0 +1,2 @@
+Add :c:func:`PyWeakref_IsDead` function, which tests if a weak reference is
+dead.
index 8d86b535effb9a7c08054a3408e5443c93d6fae0..f737250ac29d57b8ffc029f29147e1247eb808f9 100644 (file)
@@ -3144,6 +3144,7 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
     PyObject *ref = UNINITIALIZED_PTR;
     assert(PyWeakref_GetRef(weakref, &ref) == 1);
     assert(ref == obj);
+    assert(!PyWeakref_IsDead(weakref));
     assert(Py_REFCNT(obj) == (refcnt + 1));
     Py_DECREF(ref);
 
@@ -3159,6 +3160,8 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
     assert(Py_REFCNT(obj) == 1);
     Py_DECREF(obj);
 
+    assert(PyWeakref_IsDead(weakref));
+
     // test PyWeakref_GET_OBJECT(), reference is dead
     assert(PyWeakref_GET_OBJECT(weakref) == Py_None);
 
@@ -3181,6 +3184,12 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
     PyErr_Clear();
     assert(ref == NULL);
 
+    // test PyWeakRef_IsDead(), invalid type
+    assert(!PyErr_Occurred());
+    assert(PyWeakref_IsDead(invalid_weakref) == -1);
+    assert(PyErr_ExceptionMatches(PyExc_TypeError));
+    PyErr_Clear();
+
     // test PyWeakref_GetObject(), invalid type
     assert(PyWeakref_GetObject(invalid_weakref) == NULL);
     assert(PyErr_ExceptionMatches(PyExc_SystemError));
@@ -3193,6 +3202,11 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
     assert(ref == NULL);
     PyErr_Clear();
 
+    // test PyWeakref_IsDead(NULL)
+    assert(PyWeakref_IsDead(NULL) == -1);
+    assert(PyErr_ExceptionMatches(PyExc_SystemError));
+    PyErr_Clear();
+
     // test PyWeakref_GetObject(NULL)
     assert(PyWeakref_GetObject(NULL) == NULL);
     assert(PyErr_ExceptionMatches(PyExc_SystemError));
index 9e3da1c3394d5b65421b4283ced0aea79e56e337..0ee64ed70a63cdd1ad43a77607d7e33025f27c8b 100644 (file)
@@ -932,6 +932,19 @@ PyWeakref_NewProxy(PyObject *ob, PyObject *callback)
     return (PyObject *)get_or_create_weakref(type, ob, callback);
 }
 
+int
+PyWeakref_IsDead(PyObject *ref)
+{
+    if (ref == NULL) {
+        PyErr_BadInternalCall();
+        return -1;
+    }
+    if (!PyWeakref_Check(ref)) {
+        PyErr_Format(PyExc_TypeError, "expected a weakref, got %T", ref);
+        return -1;
+    }
+    return _PyWeakref_IS_DEAD(ref);
+}
 
 int
 PyWeakref_GetRef(PyObject *ref, PyObject **pobj)