Skip to content

Commit 301e3cc

Browse files
bpo-33622: Fix issues with handling errors in the GC. (GH-7078)
* Fixed a leak when the GC fails to add an object with __del__ into the gc.garbage list. * PyGC_Collect() can now be called when an exception is set and preserves it. * Fixed an undefined behavior with comparing a dead pointer with NULL.
1 parent 03c0d2e commit 301e3cc

File tree

2 files changed

+15
-8
lines changed

2 files changed

+15
-8
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fixed a leak when the garbage collector fails to add an object with the
2+
``__del__`` method or referenced by it into the :data:`gc.garbage` list.
3+
:c:func:`PyGC_Collect` can now be called when an exception is set and
4+
preserves it.

Modules/gcmodule.c

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -648,10 +648,8 @@ debug_cycle(const char *msg, PyObject *op)
648648
* garbage list (a Python list), else only the objects in finalizers with
649649
* __del__ methods are appended to garbage. All objects in finalizers are
650650
* merged into the old list regardless.
651-
* Returns 0 if all OK, <0 on error (out of memory to grow the garbage list).
652-
* The finalizers list is made empty on a successful return.
653651
*/
654-
static int
652+
static void
655653
handle_legacy_finalizers(PyGC_Head *finalizers, PyGC_Head *old)
656654
{
657655
PyGC_Head *gc = finalizers->gc.gc_next;
@@ -666,12 +664,11 @@ handle_legacy_finalizers(PyGC_Head *finalizers, PyGC_Head *old)
666664

667665
if ((_PyRuntime.gc.debug & DEBUG_SAVEALL) || has_legacy_finalizer(op)) {
668666
if (PyList_Append(_PyRuntime.gc.garbage, op) < 0)
669-
return -1;
667+
break;
670668
}
671669
}
672670

673671
gc_list_merge(finalizers, old);
674-
return 0;
675672
}
676673

677674
/* Run first-time finalizers (if any) on all the objects in collectable.
@@ -945,7 +942,7 @@ collect(int generation, Py_ssize_t *n_collected, Py_ssize_t *n_uncollectable,
945942
* reachable list of garbage. The programmer has to deal with
946943
* this if they insist on creating this type of structure.
947944
*/
948-
(void)handle_legacy_finalizers(&finalizers, old);
945+
handle_legacy_finalizers(&finalizers, old);
949946

950947
/* Clear free list only during the collection of the highest
951948
* generation */
@@ -1009,9 +1006,12 @@ invoke_gc_callback(const char *phase, int generation,
10091006
PyObject *r, *cb = PyList_GET_ITEM(_PyRuntime.gc.callbacks, i);
10101007
Py_INCREF(cb); /* make sure cb doesn't go away */
10111008
r = PyObject_CallFunction(cb, "sO", phase, info);
1012-
Py_XDECREF(r);
1013-
if (r == NULL)
1009+
if (r == NULL) {
10141010
PyErr_WriteUnraisable(cb);
1011+
}
1012+
else {
1013+
Py_DECREF(r);
1014+
}
10151015
Py_DECREF(cb);
10161016
}
10171017
Py_XDECREF(info);
@@ -1567,8 +1567,11 @@ PyGC_Collect(void)
15671567
if (_PyRuntime.gc.collecting)
15681568
n = 0; /* already collecting, don't do anything */
15691569
else {
1570+
PyObject *exc, *value, *tb;
15701571
_PyRuntime.gc.collecting = 1;
1572+
PyErr_Fetch(&exc, &value, &tb);
15711573
n = collect_with_callback(NUM_GENERATIONS - 1);
1574+
PyErr_Restore(exc, value, tb);
15721575
_PyRuntime.gc.collecting = 0;
15731576
}
15741577

0 commit comments

Comments
 (0)