Skip to content

Commit ad0aab6

Browse files
benjaminpserhiy-storchaka
authored andcommitted
[3.4] bpo-26617: Ensure gc tracking is off when invoking weakref callbacks.
(cherry picked from commit 8f657c3)
1 parent 6f6bc1d commit ad0aab6

File tree

3 files changed

+23
-12
lines changed

3 files changed

+23
-12
lines changed

Lib/test/test_weakref.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,14 @@ def test_set_callback_attribute(self):
827827
with self.assertRaises(AttributeError):
828828
ref1.__callback__ = lambda ref: None
829829

830+
def test_callback_gcs(self):
831+
class ObjectWithDel(Object):
832+
def __del__(self): pass
833+
x = ObjectWithDel(1)
834+
ref1 = weakref.ref(x, lambda ref: support.gc_collect())
835+
del x
836+
support.gc_collect()
837+
830838

831839
class SubclassableWeakrefTestCase(TestBase):
832840

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Release date: XXXX-XX-XX
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #26617: Fix crash when GC runs during weakref callbacks.
14+
1315
- bpo-27945: Fixed various segfaults with dict when input collections are
1416
mutated during searching, inserting or comparing. Based on patches by
1517
Duane Griffin and Tim Mitchell.

Objects/typeobject.c

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,11 +1116,6 @@ subtype_dealloc(PyObject *self)
11161116
Py_TRASHCAN_SAFE_BEGIN(self);
11171117
--_PyTrash_delete_nesting;
11181118
-- tstate->trash_delete_nesting;
1119-
/* DO NOT restore GC tracking at this point. weakref callbacks
1120-
* (if any, and whether directly here or indirectly in something we
1121-
* call) may trigger GC, and if self is tracked at that point, it
1122-
* will look like trash to GC and GC will try to delete self again.
1123-
*/
11241119

11251120
/* Find the nearest base with a different tp_dealloc */
11261121
base = type;
@@ -1131,30 +1126,36 @@ subtype_dealloc(PyObject *self)
11311126

11321127
has_finalizer = type->tp_finalize || type->tp_del;
11331128

1134-
/* Maybe call finalizer; exit early if resurrected */
1135-
if (has_finalizer)
1136-
_PyObject_GC_TRACK(self);
1137-
11381129
if (type->tp_finalize) {
1130+
_PyObject_GC_TRACK(self);
11391131
if (PyObject_CallFinalizerFromDealloc(self) < 0) {
11401132
/* Resurrected */
11411133
goto endlabel;
11421134
}
1135+
_PyObject_GC_UNTRACK(self);
11431136
}
1144-
/* If we added a weaklist, we clear it. Do this *before* calling
1145-
tp_del, clearing slots, or clearing the instance dict. */
1137+
/*
1138+
If we added a weaklist, we clear it. Do this *before* calling tp_del,
1139+
clearing slots, or clearing the instance dict.
1140+
1141+
GC tracking must be off at this point. weakref callbacks (if any, and
1142+
whether directly here or indirectly in something we call) may trigger GC,
1143+
and if self is tracked at that point, it will look like trash to GC and GC
1144+
will try to delete self again.
1145+
*/
11461146
if (type->tp_weaklistoffset && !base->tp_weaklistoffset)
11471147
PyObject_ClearWeakRefs(self);
11481148

11491149
if (type->tp_del) {
1150+
_PyObject_GC_TRACK(self);
11501151
type->tp_del(self);
11511152
if (self->ob_refcnt > 0) {
11521153
/* Resurrected */
11531154
goto endlabel;
11541155
}
1156+
_PyObject_GC_UNTRACK(self);
11551157
}
11561158
if (has_finalizer) {
1157-
_PyObject_GC_UNTRACK(self);
11581159
/* New weakrefs could be created during the finalizer call.
11591160
If this occurs, clear them out without calling their
11601161
finalizers since they might rely on part of the object

0 commit comments

Comments
 (0)