Skip to content

Commit 053f101

Browse files
committed
fix(structseq): track struct_time and asyncgen_hooks in GC
1 parent 60b9a41 commit 053f101

7 files changed

Lines changed: 14 additions & 4 deletions

File tree

‎Doc/c-api/tuple.rst‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,10 @@ type.
232232
Creates an instance of *type*, which must have been created with
233233
:c:func:`PyStructSequence_NewType`.
234234
235+
The returned instance is not tracked by the garbage collector. If the
236+
instance can contain objects that participate in reference cycles, call
237+
:c:func:`PyObject_GC_Track` after populating its fields.
238+
235239
Return ``NULL`` with an exception set on failure.
236240
237241

‎Lib/test/test_structseq.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -366,9 +366,11 @@ def test_replace_gc_tracked(self):
366366
self.assertTrue(gc.is_tracked(replaced_struct))
367367

368368
def test_gc_tracked(self):
369-
# PyStructSequence objects created via C API or Python should be GC-tracked
369+
# Instances created via Python or stdlib callers that may participate
370+
# in cycles are GC-tracked; simple instances like os.stat_result are not.
370371
self.assertTrue(gc.is_tracked(time.gmtime()))
371-
self.assertTrue(gc.is_tracked(os.stat(__file__)))
372+
self.assertTrue(gc.is_tracked(time.struct_time((2020, 1, 1, 0, 0, 0, 0, 1, 0))))
373+
self.assertFalse(gc.is_tracked(os.stat(__file__)))
372374

373375
if __name__ == "__main__":
374376
unittest.main()

‎Lib/test/test_sys.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1969,6 +1969,7 @@ def test_asyncgen_hooks(self):
19691969

19701970
sys.set_asyncgen_hooks(firstiter=firstiter)
19711971
hooks = sys.get_asyncgen_hooks()
1972+
self.assertTrue(gc.is_tracked(hooks))
19721973
self.assertIs(hooks.firstiter, firstiter)
19731974
self.assertIs(hooks[0], firstiter)
19741975
self.assertIs(hooks.finalizer, None)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Fix GC tracking for :class:`!PyStructSequence` instances created via :c:func:`PyStructSequence_New`. Patch by Shamil Abdulaev.
1+
Track :func:`time.struct_time` and :func:`sys.get_asyncgen_hooks` objects in the garbage collector to prevent reference cycle leaks. Patch by Shamil Abdulaev.

‎Modules/timemodule.c‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,7 @@ tmtotuple(time_module_state *state, struct tm *p
497497
#undef SET
498498
#undef SET_ITEM
499499

500+
PyObject_GC_Track(v);
500501
return v;
501502
}
502503

‎Objects/structseq.c‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ PyStructSequence_New(PyTypeObject *type)
8484
for (i = 0; i < size; i++)
8585
obj->ob_item[i] = NULL;
8686

87-
_PyObject_GC_TRACK(obj);
8887
return (PyObject*)obj;
8988
}
9089

@@ -266,6 +265,7 @@ structseq_new_impl(PyTypeObject *type, PyObject *arg, PyObject *dict)
266265
}
267266
}
268267

268+
_PyObject_GC_TRACK(res);
269269
return (PyObject*) res;
270270
}
271271

@@ -449,6 +449,7 @@ structseq_replace(PyObject *op, PyObject *args, PyObject *kwargs)
449449
}
450450
}
451451

452+
_PyObject_GC_TRACK(result);
452453
return (PyObject *)result;
453454

454455
error:

‎Python/sysmodule.c‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1531,6 +1531,7 @@ sys_get_asyncgen_hooks_impl(PyObject *module)
15311531
PyStructSequence_SET_ITEM(res, 0, Py_NewRef(firstiter));
15321532
PyStructSequence_SET_ITEM(res, 1, Py_NewRef(finalizer));
15331533

1534+
PyObject_GC_Track(res);
15341535
return res;
15351536
}
15361537

0 commit comments

Comments
 (0)