Skip to content

Commit 34fae03

Browse files
serhiy-storchakalarryhastings
authored andcommitted
[3.4] bpo-26617: Ensure gc tracking is off when invoking weakref callbacks. (#2695)
* [3.4] bpo-26617: Ensure gc tracking is off when invoking weakref callbacks. (cherry picked from commit 8f657c3) * Rewrite a NEWS entry as a NEWS.d entry.
1 parent 6f6bc1d commit 34fae03

File tree

3 files changed

+22
-12
lines changed

3 files changed

+22
-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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix crash when GC runs during weakref callbacks.

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)