-
-
Notifications
You must be signed in to change notification settings - Fork 32.7k
Description
Bug report
Bug description:
PyObject_CallFinalizerFromDealloc()
starts with a fatal assertion:
Lines 591 to 627 in 6fcac09
int | |
PyObject_CallFinalizerFromDealloc(PyObject *self) | |
{ | |
if (Py_REFCNT(self) != 0) { | |
_PyObject_ASSERT_FAILED_MSG(self, | |
"PyObject_CallFinalizerFromDealloc called " | |
"on object with a non-zero refcount"); | |
} | |
/* Temporarily resurrect the object. */ | |
_PyObject_ResurrectStart(self); | |
PyObject_CallFinalizer(self); | |
_PyObject_ASSERT_WITH_MSG(self, | |
Py_REFCNT(self) > 0, | |
"refcount is too small"); | |
_PyObject_ASSERT(self, | |
(!_PyType_IS_GC(Py_TYPE(self)) | |
|| _PyObject_GC_IS_TRACKED(self))); | |
/* Undo the temporary resurrection; can't use DECREF here, it would | |
* cause a recursive call. */ | |
if (_PyObject_ResurrectEnd(self)) { | |
/* tp_finalize resurrected it! | |
gh-130202: Note that the object may still be dead in the free | |
threaded build in some circumstances, so it's not safe to access | |
`self` after this point. For example, the last reference to the | |
resurrected `self` may be held by another thread, which can | |
concurrently deallocate it. */ | |
return -1; | |
} | |
/* this is the normal path out, the caller continues with deallocation. */ | |
return 0; | |
} |
This was ok before FT-Python, but with freethreading concurrency, it can happen that an object gets resurrected by one thread before it even learns about its own finalisation in another thread. A fatal abort of the runtime seems very unhelpful in this case.
The fact that the function calls _PyObject_ResurrectStart()
immediately after the assertion shows that the code is prepared for such a resurrection and could easily deal with it. Instead of the fatal assertion, it should check the refcount and abort the deallocation (by returning -1) if the refcount is higher than 0.
I noticed this issue while trying to port lxml to FT-Python. lxml uses unowned backlinks to Python objects from a C XML tree structure in order to assure a 1:1 mapping between C nodes and Python API proxy objects. The issues of the port are being discussed here:
lxml/lxml#477
CPython versions tested on:
3.14
Operating systems tested on:
No response