Description
Feature or enhancement
// Returns 1 if the pointed to object is dead, 0 if it's alive, and -1 with an error set if `ref` is not a weak reference.
int PyWeakref_IsDead(PyObject *ref);
C API Working Group Issue
Motivation
Prior to Python 3.13, you could check if a weak reference is dead via PyWeakref_GetObject(ref) == Py_None
, but that function is now deprecated. You might try writing an "is dead" check using PyWeakref_GetRef
. For example:
int is_dead(PyObject *ref) {
PyObject *tmp;
if (PyWeakref_GetRef(&ref, &tmp) < 0) {
return -1;
}
else if (tmp == NULL) {
return 1;
}
Py_DECREF(tmp);
return 0;
}
In addition to not being ergonomic, the problem with this code is that the Py_DECREF(tmp)
may introduce a side effect from a calling a destructor, at least in the free threading build where some other thread may concurrently drop the last reference. Our internal _PyWeakref_IS_DEAD
implementation avoids this problem, but it's not possible to reimplement that code using our existing public APIs.
This can be a problem when you need to check if a weak reference is dead within a lock, such as when cleaning up dictionaries or lists of weak references -- you don't want to execute arbitrary code via a destructor while holding the lock.
I've run into this in two C API extensions this week that are not currently thread-safe with free threading:
- CFFI uses a dictionary that maps a string keys to unowned references. I'm working on update it to use
PyWeakReference
, but the "is dead" clean-up checks are more difficult due to the above issues. - Pandas cleans up a list of weak references. (The code is not currently thread-safe, and probably needs a lock.)