Skip to content

Commit 8555091

Browse files
colesburypull[bot]
authored andcommitted
pythongh-128008: Add PyWeakref_IsDead() (pythonGH-128009)
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).
1 parent 33928bb commit 8555091

File tree

5 files changed

+41
-0
lines changed

5 files changed

+41
-0
lines changed

Doc/c-api/weakref.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,15 @@ as much as it can.
8888
Use :c:func:`PyWeakref_GetRef` instead.
8989
9090
91+
.. c:function:: int PyWeakref_IsDead(PyObject *ref)
92+
93+
Test if the weak reference *ref* is dead. Returns 1 if the reference is
94+
dead, 0 if it is alive, and -1 with an error set if *ref* is not a weak
95+
reference object.
96+
97+
.. versionadded:: 3.14
98+
99+
91100
.. c:function:: void PyObject_ClearWeakRefs(PyObject *object)
92101
93102
This function is called by the :c:member:`~PyTypeObject.tp_dealloc` handler

Include/cpython/weakrefobject.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ PyAPI_FUNC(void) _PyWeakref_ClearRef(PyWeakReference *self);
4545
#define _PyWeakref_CAST(op) \
4646
(assert(PyWeakref_Check(op)), _Py_CAST(PyWeakReference*, (op)))
4747

48+
// Test if a weak reference is dead.
49+
PyAPI_FUNC(int) PyWeakref_IsDead(PyObject *ref);
50+
4851
Py_DEPRECATED(3.13) static inline PyObject* PyWeakref_GET_OBJECT(PyObject *ref_obj)
4952
{
5053
PyWeakReference *ref = _PyWeakref_CAST(ref_obj);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add :c:func:`PyWeakref_IsDead` function, which tests if a weak reference is
2+
dead.

Modules/_testcapimodule.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3144,6 +3144,7 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
31443144
PyObject *ref = UNINITIALIZED_PTR;
31453145
assert(PyWeakref_GetRef(weakref, &ref) == 1);
31463146
assert(ref == obj);
3147+
assert(!PyWeakref_IsDead(weakref));
31473148
assert(Py_REFCNT(obj) == (refcnt + 1));
31483149
Py_DECREF(ref);
31493150

@@ -3159,6 +3160,8 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
31593160
assert(Py_REFCNT(obj) == 1);
31603161
Py_DECREF(obj);
31613162

3163+
assert(PyWeakref_IsDead(weakref));
3164+
31623165
// test PyWeakref_GET_OBJECT(), reference is dead
31633166
assert(PyWeakref_GET_OBJECT(weakref) == Py_None);
31643167

@@ -3181,6 +3184,12 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
31813184
PyErr_Clear();
31823185
assert(ref == NULL);
31833186

3187+
// test PyWeakRef_IsDead(), invalid type
3188+
assert(!PyErr_Occurred());
3189+
assert(PyWeakref_IsDead(invalid_weakref) == -1);
3190+
assert(PyErr_ExceptionMatches(PyExc_TypeError));
3191+
PyErr_Clear();
3192+
31843193
// test PyWeakref_GetObject(), invalid type
31853194
assert(PyWeakref_GetObject(invalid_weakref) == NULL);
31863195
assert(PyErr_ExceptionMatches(PyExc_SystemError));
@@ -3193,6 +3202,11 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
31933202
assert(ref == NULL);
31943203
PyErr_Clear();
31953204

3205+
// test PyWeakref_IsDead(NULL)
3206+
assert(PyWeakref_IsDead(NULL) == -1);
3207+
assert(PyErr_ExceptionMatches(PyExc_SystemError));
3208+
PyErr_Clear();
3209+
31963210
// test PyWeakref_GetObject(NULL)
31973211
assert(PyWeakref_GetObject(NULL) == NULL);
31983212
assert(PyErr_ExceptionMatches(PyExc_SystemError));

Objects/weakrefobject.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,19 @@ PyWeakref_NewProxy(PyObject *ob, PyObject *callback)
932932
return (PyObject *)get_or_create_weakref(type, ob, callback);
933933
}
934934

935+
int
936+
PyWeakref_IsDead(PyObject *ref)
937+
{
938+
if (ref == NULL) {
939+
PyErr_BadInternalCall();
940+
return -1;
941+
}
942+
if (!PyWeakref_Check(ref)) {
943+
PyErr_Format(PyExc_TypeError, "expected a weakref, got %T", ref);
944+
return -1;
945+
}
946+
return _PyWeakref_IS_DEAD(ref);
947+
}
935948

936949
int
937950
PyWeakref_GetRef(PyObject *ref, PyObject **pobj)

0 commit comments

Comments
 (0)