Skip to content

Commit dcb687c

Browse files
authored
[3.14] gh-161419: fix weakref reviving a dying object in free-threading (GH-161431) (#161483)
(cherry picked from commit 85a67ba)
1 parent 4ac9b71 commit dcb687c

3 files changed

Lines changed: 29 additions & 0 deletions

File tree

‎Include/internal/pycore_object.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,8 @@ _PyObject_ResurrectEnd(PyObject *op)
742742
if (_Py_IsOwnedByCurrentThread(op) && local == 1 && shared == 0) {
743743
// Fast-path: object has a single refcount and is owned by this thread
744744
_Py_atomic_store_uint32_relaxed(&op->ob_ref_local, 0);
745+
_Py_atomic_store_uintptr_relaxed(&op->ob_tid, _Py_UNOWNED_TID);
746+
_Py_atomic_store_ssize_relaxed(&op->ob_ref_shared, _Py_REF_MERGED);
745747
# ifdef Py_TRACE_REFS
746748
_Py_ForgetReference(op);
747749
# endif

‎Lib/test/test_weakref.py‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import gc
2+
import io
23
import sys
34
import doctest
45
import unittest
@@ -1012,6 +1013,29 @@ def cb(wparent):
10121013
del root
10131014
gc.collect()
10141015

1016+
def test_ref_to_object_dying_after_finalizer(self):
1017+
cleared = []
1018+
class Attr:
1019+
def __del__(self):
1020+
cleared.append(self.wr() is None)
1021+
1022+
class SetWithDel(set):
1023+
__slots__ = ('a',)
1024+
def __del__(self):
1025+
pass
1026+
1027+
class Raw(io.RawIOBase):
1028+
__slots__ = ('a',)
1029+
1030+
for cls in (SetWithDel, Raw):
1031+
with self.subTest(cls=cls.__name__):
1032+
cleared.clear()
1033+
o = cls()
1034+
o.a = Attr()
1035+
o.a.wr = weakref.ref(o)
1036+
del o
1037+
self.assertEqual(cleared, [True])
1038+
10151039
def test_callback_attribute(self):
10161040
x = Object(1)
10171041
callback = lambda ref: None
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a crash in the :term:`free-threaded build` where a weak reference could
2+
revive an object whose finalizer had already run during deallocation, causing
3+
it to be deallocated twice.

0 commit comments

Comments
 (0)