Skip to content

Add PyWeakref_IsDead() to test if a weak reference is dead #48

@colesbury

Description

@colesbury

EDIT: Added, -1 error return case per @vstinner's suggestion.

EDIT 2: @vstinner added a vote.

I propose adding a dedicated C API function to check if a weak reference is dead:

// 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);

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.)

Vote

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions