Skip to content

Commit 9e2d93c

Browse files
committed
Fix freezing and GC untracking
1 parent ab1faec commit 9e2d93c

File tree

2 files changed

+13
-27
lines changed

2 files changed

+13
-27
lines changed

Lib/test/test_gc.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,7 +1284,8 @@ def test_refcount_errors(self):
12841284
from test.support import gc_collect, SuppressCrashReport
12851285
12861286
a = [1, 2, 3]
1287-
b = [a]
1287+
b = [a, a]
1288+
a.append(b)
12881289
12891290
# Avoid coredump when Py_FatalError() calls abort()
12901291
SuppressCrashReport().__enter__()
@@ -1294,6 +1295,8 @@ def test_refcount_errors(self):
12941295
# (to avoid deallocating it):
12951296
import ctypes
12961297
ctypes.pythonapi.Py_DecRef(ctypes.py_object(a))
1298+
del a
1299+
del b
12971300
12981301
# The garbage collector should now have a fatal error
12991302
# when it reaches the broken object
@@ -1322,7 +1325,7 @@ def test_refcount_errors(self):
13221325
self.assertRegex(stderr,
13231326
br'object type name: list')
13241327
self.assertRegex(stderr,
1325-
br'object repr : \[1, 2, 3\]')
1328+
br'object repr : \[1, 2, 3, \[\[...\], \[...\]\]\]')
13261329

13271330

13281331
class GCTogglingTests(unittest.TestCase):

Python/gc.c

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,7 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
726726
}
727727

728728
static void
729-
untrack_tuples(PyGC_Head *head)
729+
untrack_tuples_and_dicts(PyGC_Head *head)
730730
{
731731
PyGC_Head *next, *gc = GC_NEXT(head);
732732
while (gc != head) {
@@ -735,19 +735,7 @@ untrack_tuples(PyGC_Head *head)
735735
if (PyTuple_CheckExact(op)) {
736736
_PyTuple_MaybeUntrack(op);
737737
}
738-
gc = next;
739-
}
740-
}
741-
742-
/* Try to untrack all currently tracked dictionaries */
743-
static void
744-
untrack_dicts(PyGC_Head *head)
745-
{
746-
PyGC_Head *next, *gc = GC_NEXT(head);
747-
while (gc != head) {
748-
PyObject *op = FROM_GC(gc);
749-
next = GC_NEXT(gc);
750-
if (PyDict_CheckExact(op)) {
738+
else if (PyDict_CheckExact(op)) {
751739
_PyDict_MaybeUntrack(op);
752740
}
753741
gc = next;
@@ -1307,6 +1295,7 @@ gc_collect_young(PyThreadState *tstate,
13071295
GCState *gcstate = &tstate->interp->gc;
13081296
PyGC_Head *young = &gcstate->young.head;
13091297
PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head;
1298+
untrack_tuples_and_dicts(&gcstate->young.head);
13101299
GC_STAT_ADD(0, collections, 1);
13111300
#ifdef Py_STATS
13121301
{
@@ -1361,7 +1350,6 @@ IS_IN_VISITED(PyGC_Head *gc, int visited_space)
13611350
struct container_and_flag {
13621351
PyGC_Head *container;
13631352
int visited_space;
1364-
int mark;
13651353
uintptr_t size;
13661354
};
13671355

@@ -1392,7 +1380,6 @@ expand_region_transitively_reachable(PyGC_Head *container, PyGC_Head *gc, GCStat
13921380
struct container_and_flag arg = {
13931381
.container = container,
13941382
.visited_space = gcstate->visited_space,
1395-
.mark = 0,
13961383
.size = 0
13971384
};
13981385
assert(GC_NEXT(gc) == container);
@@ -1458,7 +1445,6 @@ mark_all_reachable(PyGC_Head *reachable, PyGC_Head *visited, int visited_space)
14581445
struct container_and_flag arg = {
14591446
.container = reachable,
14601447
.visited_space = visited_space,
1461-
.mark = 1,
14621448
.size = 0
14631449
};
14641450
while (!gc_list_is_empty(reachable)) {
@@ -1590,6 +1576,7 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
15901576
GC_STAT_ADD(1, objects_transitively_reachable, objects_marked);
15911577
gcstate->work_to_do -= objects_marked;
15921578
gc_list_set_space(&gcstate->young.head, gcstate->visited_space);
1579+
untrack_tuples_and_dicts(&gcstate->young.head);
15931580
gc_list_merge(&gcstate->young.head, &increment);
15941581
gcstate->young.count = 0;
15951582
gc_list_validate_space(&increment, gcstate->visited_space);
@@ -1613,7 +1600,7 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
16131600
gc_list_validate_space(&survivors, gcstate->visited_space);
16141601
gc_list_merge(&survivors, visited);
16151602
assert(gc_list_is_empty(&increment));
1616-
gcstate->work_to_do += gcstate->heap_size / SCAN_RATE_DIVISOR/ 2 / scale_factor;
1603+
gcstate->work_to_do += gcstate->heap_size / SCAN_RATE_DIVISOR / scale_factor;
16171604
gcstate->work_to_do -= increment_size;
16181605

16191606
validate_old(gcstate);
@@ -1634,6 +1621,7 @@ gc_collect_full(PyThreadState *tstate,
16341621
PyGC_Head *young = &gcstate->young.head;
16351622
PyGC_Head *pending = &gcstate->old[gcstate->visited_space^1].head;
16361623
PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head;
1624+
untrack_tuples_and_dicts(&gcstate->young.head);
16371625
/* merge all generations into pending */
16381626
gc_list_validate_space(young, 1-gcstate->visited_space);
16391627
gc_list_merge(young, pending);
@@ -1680,12 +1668,6 @@ gc_collect_region(PyThreadState *tstate,
16801668
gc_list_init(&unreachable);
16811669
deduce_unreachable(from, &unreachable);
16821670
validate_consistent_old_space(from);
1683-
if (untrack & UNTRACK_TUPLES) {
1684-
untrack_tuples(from);
1685-
}
1686-
if (untrack & UNTRACK_DICTS) {
1687-
untrack_dicts(from);
1688-
}
16891671
validate_consistent_old_space(to);
16901672
if (from != to) {
16911673
gc_list_merge(from, to);
@@ -1905,9 +1887,10 @@ _PyGC_Freeze(PyInterpreterState *interp)
19051887
{
19061888
GCState *gcstate = &interp->gc;
19071889
/* The permanent_generation has its old space bit set to zero */
1908-
if (gcstate->visited_space) {
1890+
if (!gcstate->visited_space) {
19091891
gc_list_set_space(&gcstate->young.head, 0);
19101892
}
1893+
gc_list_validate_space(&gcstate->young.head, 0);
19111894
gc_list_merge(&gcstate->young.head, &gcstate->permanent_generation.head);
19121895
gcstate->young.count = 0;
19131896
PyGC_Head*old0 = &gcstate->old[0].head;

0 commit comments

Comments
 (0)