Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Include/internal/pycore_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,8 @@ _PyObject_ResurrectEnd(PyObject *op)
if (_Py_IsOwnedByCurrentThread(op) && local == 1 && shared == 0) {
// Fast-path: object has a single refcount and is owned by this thread
_Py_atomic_store_uint32_relaxed(&op->ob_ref_local, 0);
_Py_atomic_store_uintptr_relaxed(&op->ob_tid, _Py_UNOWNED_TID);
_Py_atomic_store_ssize_relaxed(&op->ob_ref_shared, _Py_REF_MERGED);
# ifdef Py_TRACE_REFS
_Py_ForgetReference(op);
# endif
Expand Down
24 changes: 24 additions & 0 deletions Lib/test/test_weakref.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import gc
import io
import sys
import doctest
import unittest
Expand Down Expand Up @@ -1056,6 +1057,29 @@ def __del__(self):
del head
self.assertEqual(found, [])

def test_ref_to_object_dying_after_finalizer(self):
cleared = []
class Attr:
def __del__(self):
cleared.append(self.wr() is None)

class SetWithDel(set):
__slots__ = ('a',)
def __del__(self):
pass

class Raw(io.RawIOBase):
__slots__ = ('a',)

for cls in (SetWithDel, Raw):
with self.subTest(cls=cls.__name__):
cleared.clear()
o = cls()
o.a = Attr()
o.a.wr = weakref.ref(o)
del o
self.assertEqual(cleared, [True])

def test_callback_attribute(self):
x = Object(1)
callback = lambda ref: None
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix a crash in the :term:`free-threaded build` where a weak reference could
revive an object whose finalizer had already run during deallocation, causing
it to be deallocated twice.
Loading