From 95fe67e5724f3eff9b17c085edcc4fec21338348 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sun, 29 Sep 2024 20:45:11 +0200 Subject: [PATCH 01/15] Revert "[3.13] GH-117759: Document incremental GC (GH-123266) (#123395)" This reverts commit aca65112fe3e0d2e65a73d98186ac0309a70b65d. --- Doc/library/gc.rst | 55 ++++++++++++------------------------------- Doc/whatsnew/3.13.rst | 13 ---------- Python/gc.c | 27 +++++++++------------ 3 files changed, 26 insertions(+), 69 deletions(-) diff --git a/Doc/library/gc.rst b/Doc/library/gc.rst index 1065ec30802841..790dfdfd00b196 100644 --- a/Doc/library/gc.rst +++ b/Doc/library/gc.rst @@ -40,18 +40,11 @@ The :mod:`gc` module provides the following functions: .. function:: collect(generation=2) - Perform a collection. The optional argument *generation* + With no arguments, run a full collection. The optional argument *generation* may be an integer specifying which generation to collect (from 0 to 2). A - :exc:`ValueError` is raised if the generation number is invalid. The sum of + :exc:`ValueError` is raised if the generation number is invalid. The sum of collected objects and uncollectable objects is returned. - Calling ``gc.collect(0)`` will perform a GC collection on the young generation. - - Calling ``gc.collect(1)`` will perform a GC collection on the young generation - and an increment of the old generation. - - Calling ``gc.collect(2)`` or ``gc.collect()`` performs a full collection - The free lists maintained for a number of built-in types are cleared whenever a full collection or collection of the highest generation (2) is run. Not all items in some free lists may be freed due to the @@ -60,9 +53,6 @@ The :mod:`gc` module provides the following functions: The effect of calling ``gc.collect()`` while the interpreter is already performing a collection is undefined. - .. versionchanged:: 3.13 - ``generation=1`` performs an increment of collection. - .. function:: set_debug(flags) @@ -78,20 +68,13 @@ The :mod:`gc` module provides the following functions: .. function:: get_objects(generation=None) - Returns a list of all objects tracked by the collector, excluding the list - returned. If *generation* is not ``None``, return only the objects as follows: - - * 0: All objects in the young generation - * 1: No objects, as there is no generation 1 (as of Python 3.13) - * 2: All objects in the old generation + returned. If *generation* is not ``None``, return only the objects tracked by + the collector that are in that generation. .. versionchanged:: 3.8 New *generation* parameter. - .. versionchanged:: 3.13 - Generation 1 is removed - .. audit-event:: gc.get_objects generation gc.get_objects .. function:: get_stats() @@ -118,27 +101,19 @@ The :mod:`gc` module provides the following functions: Set the garbage collection thresholds (the collection frequency). Setting *threshold0* to zero disables collection. - The GC classifies objects into two generations depending on whether they have - survived a collection. New objects are placed in the young generation. If an - object survives a collection it is moved into the old generation. - - In order to decide when to run, the collector keeps track of the number of object + The GC classifies objects into three generations depending on how many + collection sweeps they have survived. New objects are placed in the youngest + generation (generation ``0``). If an object survives a collection it is moved + into the next older generation. Since generation ``2`` is the oldest + generation, objects in that generation remain there after a collection. In + order to decide when to run, the collector keeps track of the number object allocations and deallocations since the last collection. When the number of allocations minus the number of deallocations exceeds *threshold0*, collection - starts. For each collection, all the objects in the young generation and some - fraction of the old generation is collected. - - The fraction of the old generation that is collected is **inversely** proportional - to *threshold1*. The larger *threshold1* is, the slower objects in the old generation - are collected. - For the default value of 10, 1% of the old generation is scanned during each collection. - - *threshold2* is ignored. - - See `Garbage collector design `_ for more information. - - .. versionchanged:: 3.13 - *threshold2* is ignored + starts. Initially only generation ``0`` is examined. If generation ``0`` has + been examined more than *threshold1* times since generation ``1`` has been + examined, then generation ``1`` is examined as well. + With the third generation, things are a bit more complicated, + see `Collecting the oldest generation `_ for more information. .. function:: get_count() diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 52fe749697cfa4..05d99064db2486 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -509,19 +509,6 @@ Incremental garbage collection The cycle garbage collector is now incremental. This means that maximum pause times are reduced by an order of magnitude or more for larger heaps. - -There are now only two generations: young and old. -When :func:`gc.collect` is not called directly, the -GC is invoked a little less frequently. When invoked, it -collects the young generation and an increment of the -old generation, instead of collecting one or more generations. - -The behavior of :func:`!gc.collect` changes slightly: - -* ``gc.collect(1)``: Performs an increment of garbage collection, - rather than collecting generation 1. -* Other calls to :func:`!gc.collect` are unchanged. - (Contributed by Mark Shannon in :gh:`108362`.) diff --git a/Python/gc.c b/Python/gc.c index b79d4b5332ea83..74111c19884303 100644 --- a/Python/gc.c +++ b/Python/gc.c @@ -1701,25 +1701,20 @@ _PyGC_GetObjects(PyInterpreterState *interp, int generation) GCState *gcstate = &interp->gc; PyObject *result = PyList_New(0); - /* Generation: - * -1: Return all objects - * 0: All young objects - * 1: No objects - * 2: All old objects - */ - if (result == NULL || generation == 1) { - return result; + if (result == NULL) { + return NULL; } - if (generation <= 0) { - if (append_objects(result, &gcstate->young.head)) { - goto error; + + if (generation == -1) { + /* If generation is -1, get all objects from all generations */ + for (int i = 0; i < NUM_GENERATIONS; i++) { + if (append_objects(result, GEN_HEAD(gcstate, i))) { + goto error; + } } } - if (generation != 0) { - if (append_objects(result, &gcstate->old[0].head)) { - goto error; - } - if (append_objects(result, &gcstate->old[1].head)) { + else { + if (append_objects(result, GEN_HEAD(gcstate, generation))) { goto error; } } From 8e9333e00f75b71e0eea3947ac397ac2f9ded051 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sun, 29 Sep 2024 20:45:33 +0200 Subject: [PATCH 02/15] Revert "[3.13] GH-122298: Restore printing of GC stats (GH-123261) (#123268)" This reverts commit b1372e2f1afe6bd89ffaebe7383418acd8171408. --- Python/gc.c | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/Python/gc.c b/Python/gc.c index 74111c19884303..8850f8ef71332c 100644 --- a/Python/gc.c +++ b/Python/gc.c @@ -1289,7 +1289,6 @@ gc_collect_young(PyThreadState *tstate, GCState *gcstate = &tstate->interp->gc; PyGC_Head *young = &gcstate->young.head; PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head; - GC_STAT_ADD(0, collections, 1); #ifdef Py_STATS { Py_ssize_t count = 0; @@ -1418,7 +1417,6 @@ completed_cycle(GCState *gcstate) static void gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats) { - GC_STAT_ADD(1, collections, 1); GCState *gcstate = &tstate->interp->gc; PyGC_Head *not_visited = &gcstate->old[gcstate->visited_space^1].head; PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head; @@ -1464,7 +1462,6 @@ static void gc_collect_full(PyThreadState *tstate, struct gc_collection_stats *stats) { - GC_STAT_ADD(2, collections, 1); GCState *gcstate = &tstate->interp->gc; validate_old(gcstate); PyGC_Head *young = &gcstate->young.head; @@ -1787,24 +1784,6 @@ PyGC_IsEnabled(void) return gcstate->enabled; } -// Show stats for objects in each generations -static void -show_stats_each_generations(GCState *gcstate) -{ - char buf[100]; - size_t pos = 0; - - for (int i = 0; i < NUM_GENERATIONS && pos < sizeof(buf); i++) { - pos += PyOS_snprintf(buf+pos, sizeof(buf)-pos, - " %zd", - gc_list_size(GEN_HEAD(gcstate, i))); - } - PySys_FormatStderr( - "gc: objects in each generation:%s\n" - "gc: objects in permanent generation: %zd\n", - buf, gc_list_size(&gcstate->permanent_generation.head)); -} - Py_ssize_t _PyGC_Collect(PyThreadState *tstate, int generation, _PyGC_Reason reason) { @@ -1820,10 +1799,6 @@ _PyGC_Collect(PyThreadState *tstate, int generation, _PyGC_Reason reason) if (reason != _Py_GC_REASON_SHUTDOWN) { invoke_gc_callback(gcstate, "start", generation, &stats); } - if (gcstate->debug & _PyGC_DEBUG_STATS) { - PySys_WriteStderr("gc: collecting generation %d...\n", generation); - show_stats_each_generations(gcstate); - } if (PyDTrace_GC_START_ENABLED()) { PyDTrace_GC_START(generation); } From 3155e4480e54a0b4601d8a2f33a077a98e4899f1 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sun, 29 Sep 2024 20:47:44 +0200 Subject: [PATCH 03/15] Revert "gh-117657: TSAN fix race on `gstate->young.count` (#118313)" This reverts commit 2ba1aed596ff9e7c5e193cf990f1f20f08bbf116. --- Python/gc_free_threading.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index d1d5664ab96f33..8a20c4b233257a 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -1090,20 +1090,9 @@ record_deallocation(PyThreadState *tstate) } static void -gc_collect_internal(PyInterpreterState *interp, struct collection_state *state, int generation) +gc_collect_internal(PyInterpreterState *interp, struct collection_state *state) { _PyEval_StopTheWorld(interp); - - // update collection and allocation counters - if (generation+1 < NUM_GENERATIONS) { - state->gcstate->old[generation].count += 1; - } - - state->gcstate->young.count = 0; - for (int i = 1; i <= generation; ++i) { - state->gcstate->old[i-1].count = 0; - } - // merge refcounts for all queued objects merge_all_queued_objects(interp, state); process_delayed_frees(interp); @@ -1169,6 +1158,7 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state, static Py_ssize_t gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) { + int i; Py_ssize_t m = 0; /* # objects collected */ Py_ssize_t n = 0; /* # unreachable objects that couldn't be collected */ PyTime_t t1 = 0; /* initialize to prevent a compiler warning */ @@ -1215,6 +1205,15 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) PyDTrace_GC_START(generation); } + /* update collection and allocation counters */ + if (generation+1 < NUM_GENERATIONS) { + gcstate->old[generation].count += 1; + } + gcstate->young.count = 0; + for (i = 1; i <= generation; i++) { + gcstate->old[i-1].count = 0; + } + PyInterpreterState *interp = tstate->interp; struct collection_state state = { @@ -1222,7 +1221,7 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) .gcstate = gcstate, }; - gc_collect_internal(interp, &state, generation); + gc_collect_internal(interp, &state); m = state.collected; n = state.uncollectable; From 8f60f05ca8f348b39b116af6c90fb49eac72d746 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sun, 29 Sep 2024 20:48:08 +0200 Subject: [PATCH 04/15] Revert "Silence compiler warnings in gc.c (#117422)" This reverts commit ddf814db744006e0f42328aa15ace97c9d8ad681. --- Python/gc.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/Python/gc.c b/Python/gc.c index 8850f8ef71332c..22f12a03f6bafd 100644 --- a/Python/gc.c +++ b/Python/gc.c @@ -1316,7 +1316,6 @@ gc_collect_young(PyThreadState *tstate, survivor_count++; } } - (void)survivor_count; // Silence compiler warning gc_list_merge(&survivors, visited); validate_old(gcstate); gcstate->young.count = 0; @@ -1329,14 +1328,12 @@ gc_collect_young(PyThreadState *tstate, add_stats(gcstate, 0, stats); } -#ifndef NDEBUG static inline int IS_IN_VISITED(PyGC_Head *gc, int visited_space) { assert(visited_space == 0 || flip_old_space(visited_space) == 0); return gc_old_space(gc) == visited_space; } -#endif struct container_and_flag { PyGC_Head *container; From 299228923496f6e69f6d73ccc2ebe8d78a528f8f Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sun, 29 Sep 2024 20:48:56 +0200 Subject: [PATCH 05/15] Revert "GH-117108: Set the "old space bit" to "visited" for all young objects (#117213)" This reverts commit 8bef34f625e21886b1c64544c060e19ee2e229bf. --- Include/internal/pycore_gc.h | 14 +-------- Include/internal/pycore_object.h | 4 +-- Lib/test/test_gc.py | 24 +++++++++++++- Python/gc.c | 54 ++++++++++---------------------- 4 files changed, 43 insertions(+), 53 deletions(-) diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index 28e34d3809634c..b167f0966dd21b 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -146,19 +146,7 @@ static inline void _PyObject_GC_SET_SHARED_INLINE(PyObject *op) { /* Bit 1 is set when the object is in generation which is GCed currently. */ #define _PyGC_PREV_MASK_COLLECTING 2 -/* Bit 0 in _gc_next is the old space bit. - * It is set as follows: - * Young: gcstate->visited_space - * old[0]: 0 - * old[1]: 1 - * permanent: 0 - * - * During a collection all objects handled should have the bit set to - * gcstate->visited_space, as objects are moved from the young gen - * and the increment into old[gcstate->visited_space]. - * When object are moved from the pending space, old[gcstate->visited_space^1] - * into the increment, the old space bit is flipped. -*/ +/* Bit 0 is set if the object belongs to old space 1 */ #define _PyGC_NEXT_MASK_OLD_SPACE_1 1 #define _PyGC_PREV_SHIFT 2 diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 62d63878082386..7862cb1bafa9d4 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -357,8 +357,8 @@ static inline void _PyObject_GC_TRACK( PyGC_Head *last = (PyGC_Head*)(generation0->_gc_prev); _PyGCHead_SET_NEXT(last, gc); _PyGCHead_SET_PREV(gc, last); - /* Young objects will be moved into the visited space during GC, so set the bit here */ - gc->_gc_next = ((uintptr_t)generation0) | interp->gc.visited_space; + _PyGCHead_SET_NEXT(gc, generation0); + assert((gc->_gc_next & _PyGC_NEXT_MASK_OLD_SPACE_1) == 0); generation0->_gc_prev = (uintptr_t)gc; #endif } diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index bb7df1f5cfa7f7..4386a58bc5fa78 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -835,10 +835,32 @@ def test_get_objects_generations(self): self.assertTrue( any(l is element for element in gc.get_objects(generation=0)) ) - gc.collect() + self.assertFalse( + any(l is element for element in gc.get_objects(generation=1)) + ) + self.assertFalse( + any(l is element for element in gc.get_objects(generation=2)) + ) + gc.collect(generation=0) + self.assertFalse( + any(l is element for element in gc.get_objects(generation=0)) + ) + self.assertTrue( + any(l is element for element in gc.get_objects(generation=1)) + ) + self.assertFalse( + any(l is element for element in gc.get_objects(generation=2)) + ) + gc.collect(generation=2) self.assertFalse( any(l is element for element in gc.get_objects(generation=0)) ) + self.assertFalse( + any(l is element for element in gc.get_objects(generation=1)) + ) + self.assertTrue( + any(l is element for element in gc.get_objects(generation=2)) + ) del l gc.collect() diff --git a/Python/gc.c b/Python/gc.c index 22f12a03f6bafd..c39acb69951a72 100644 --- a/Python/gc.c +++ b/Python/gc.c @@ -454,20 +454,10 @@ validate_consistent_old_space(PyGC_Head *head) assert(prev == GC_PREV(head)); } -static void -gc_list_validate_space(PyGC_Head *head, int space) { - PyGC_Head *gc = GC_NEXT(head); - while (gc != head) { - assert(gc_old_space(gc) == space); - gc = GC_NEXT(gc); - } -} - #else #define validate_list(x, y) do{}while(0) #define validate_old(g) do{}while(0) #define validate_consistent_old_space(l) do{}while(0) -#define gc_list_validate_space(l, s) do{}while(0) #endif /*** end of list stuff ***/ @@ -958,7 +948,6 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old) /* Invoke the callbacks we decided to honor. It's safe to invoke them * because they can't reference unreachable objects. */ - int visited_space = get_gc_state()->visited_space; while (! gc_list_is_empty(&wrcb_to_call)) { PyObject *temp; PyObject *callback; @@ -993,7 +982,6 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old) Py_DECREF(op); if (wrcb_to_call._gc_next == (uintptr_t)gc) { /* object is still alive -- move it */ - gc_set_old_space(gc, visited_space); gc_list_move(gc, old); } else { @@ -1400,14 +1388,6 @@ completed_cycle(GCState *gcstate) assert(gc_list_is_empty(not_visited)); #endif gcstate->visited_space = flip_old_space(gcstate->visited_space); - /* Make sure all young objects have old space bit set correctly */ - PyGC_Head *young = &gcstate->young.head; - PyGC_Head *gc = GC_NEXT(young); - while (gc != young) { - PyGC_Head *next = GC_NEXT(gc); - gc_set_old_space(gc, gcstate->visited_space); - gc = next; - } gcstate->work_to_do = 0; } @@ -1425,7 +1405,10 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats) } gc_list_merge(&gcstate->young.head, &increment); gcstate->young.count = 0; - gc_list_validate_space(&increment, gcstate->visited_space); + if (gcstate->visited_space) { + /* objects in visited space have bit set, so we set it here */ + gc_list_set_space(&increment, 1); + } Py_ssize_t increment_size = 0; while (increment_size < gcstate->work_to_do) { if (gc_list_is_empty(not_visited)) { @@ -1437,11 +1420,9 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats) gc_set_old_space(gc, gcstate->visited_space); increment_size += expand_region_transitively_reachable(&increment, gc, gcstate); } - gc_list_validate_space(&increment, gcstate->visited_space); PyGC_Head survivors; gc_list_init(&survivors); gc_collect_region(tstate, &increment, &survivors, UNTRACK_TUPLES, stats); - gc_list_validate_space(&survivors, gcstate->visited_space); gc_list_merge(&survivors, visited); assert(gc_list_is_empty(&increment)); gcstate->work_to_do += gcstate->heap_size / SCAN_RATE_DIVISOR / scale_factor; @@ -1462,18 +1443,23 @@ gc_collect_full(PyThreadState *tstate, GCState *gcstate = &tstate->interp->gc; validate_old(gcstate); PyGC_Head *young = &gcstate->young.head; - PyGC_Head *pending = &gcstate->old[gcstate->visited_space^1].head; - PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head; - /* merge all generations into visited */ - gc_list_validate_space(young, gcstate->visited_space); - gc_list_set_space(pending, gcstate->visited_space); - gc_list_merge(young, pending); + PyGC_Head *old0 = &gcstate->old[0].head; + PyGC_Head *old1 = &gcstate->old[1].head; + /* merge all generations into old0 */ + gc_list_merge(young, old0); gcstate->young.count = 0; - gc_list_merge(pending, visited); + PyGC_Head *gc = GC_NEXT(old1); + while (gc != old1) { + PyGC_Head *next = GC_NEXT(gc); + gc_set_old_space(gc, 0); + gc = next; + } + gc_list_merge(old1, old0); - gc_collect_region(tstate, visited, visited, + gc_collect_region(tstate, old0, old0, UNTRACK_TUPLES | UNTRACK_DICTS, stats); + gcstate->visited_space = 1; gcstate->young.count = 0; gcstate->old[0].count = 0; gcstate->old[1].count = 0; @@ -1540,7 +1526,6 @@ gc_collect_region(PyThreadState *tstate, /* Clear weakrefs and invoke callbacks as necessary. */ stats->collected += handle_weakrefs(&unreachable, to); - gc_list_validate_space(to, gcstate->visited_space); validate_list(to, collecting_clear_unreachable_clear); validate_list(&unreachable, collecting_set_unreachable_clear); @@ -1574,7 +1559,6 @@ gc_collect_region(PyThreadState *tstate, * this if they insist on creating this type of structure. */ handle_legacy_finalizers(tstate, gcstate, &finalizers, to); - gc_list_validate_space(to, gcstate->visited_space); validate_list(to, collecting_clear_unreachable_clear); } @@ -1723,10 +1707,6 @@ void _PyGC_Freeze(PyInterpreterState *interp) { GCState *gcstate = &interp->gc; - /* The permanent_generation has its old space bit set to zero */ - if (gcstate->visited_space) { - gc_list_set_space(&gcstate->young.head, 0); - } gc_list_merge(&gcstate->young.head, &gcstate->permanent_generation.head); gcstate->young.count = 0; PyGC_Head*old0 = &gcstate->old[0].head; From 60a248bdfc406558216367bf0eda0422746914ef Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sun, 29 Sep 2024 20:54:16 +0200 Subject: [PATCH 06/15] Revert "GH-117108: Change the size of the GC increment to about 1% of the total heap size. (GH-117120)" This reverts commit e28477f214276db941e715eebc8cdfb96c1207d9. --- Include/internal/pycore_gc.h | 3 +-- Lib/test/test_gc.py | 23 ++++++++++------------- Modules/gcmodule.c | 2 +- Python/gc.c | 30 +++++++++++++++--------------- Python/gc_free_threading.c | 2 +- 5 files changed, 28 insertions(+), 32 deletions(-) diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index b167f0966dd21b..2d8ed5ddcb4383 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -315,7 +315,6 @@ struct _gc_runtime_state { /* a list of callbacks to be invoked when collection is performed */ PyObject *callbacks; - Py_ssize_t heap_size; Py_ssize_t work_to_do; /* Which of the old spaces is the visited space */ int visited_space; @@ -363,7 +362,7 @@ extern void _PyGC_Unfreeze(PyInterpreterState *interp); /* Number of frozen objects */ extern Py_ssize_t _PyGC_GetFreezeCount(PyInterpreterState *interp); -extern PyObject *_PyGC_GetObjects(PyInterpreterState *interp, int generation); +extern PyObject *_PyGC_GetObjects(PyInterpreterState *interp, Py_ssize_t generation); extern PyObject *_PyGC_GetReferrers(PyInterpreterState *interp, PyObject *objs); // Functions to clear types free lists diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index 4386a58bc5fa78..65742612310f80 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -1122,31 +1122,28 @@ def make_ll(depth): head = LinkedList(head, head.prev) return head - head = make_ll(1000) - count = 1000 + head = make_ll(10000) + count = 10000 - # There will be some objects we aren't counting, - # e.g. the gc stats dicts. This test checks - # that the counts don't grow, so we try to - # correct for the uncounted objects - # This is just an estimate. - CORRECTION = 20 + # We expect the counts to go negative eventually + # as there will some objects we aren't counting, + # e.g. the gc stats dicts. The test merely checks + # that the counts don't grow. enabled = gc.isenabled() gc.enable() olds = [] - for i in range(20_000): - newhead = make_ll(20) - count += 20 + for i in range(1000): + newhead = make_ll(200) + count += 200 newhead.surprise = head olds.append(newhead) - if len(olds) == 20: + if len(olds) == 50: stats = gc.get_stats() young = stats[0] incremental = stats[1] old = stats[2] collected = young['collected'] + incremental['collected'] + old['collected'] - count += CORRECTION live = count - collected self.assertLess(live, 25000) del olds[:] diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 57e4aae9ed557e..fe609286efcdf4 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -326,7 +326,7 @@ gc_get_objects_impl(PyObject *module, Py_ssize_t generation) } PyInterpreterState *interp = _PyInterpreterState_GET(); - return _PyGC_GetObjects(interp, (int)generation); + return _PyGC_GetObjects(interp, generation); } /*[clinic input] diff --git a/Python/gc.c b/Python/gc.c index c39acb69951a72..27c23f5b019c8e 100644 --- a/Python/gc.c +++ b/Python/gc.c @@ -181,7 +181,6 @@ _PyGC_Init(PyInterpreterState *interp) if (gcstate->callbacks == NULL) { return _PyStatus_NO_MEMORY(); } - gcstate->heap_size = 0; return _PyStatus_OK(); } @@ -1232,7 +1231,7 @@ gc_collect_region(PyThreadState *tstate, struct gc_collection_stats *stats); static inline Py_ssize_t -gc_list_set_space(PyGC_Head *list, int space) +gc_list_set_space(PyGC_Head *list, uintptr_t space) { Py_ssize_t size = 0; PyGC_Head *gc; @@ -1258,9 +1257,9 @@ gc_list_set_space(PyGC_Head *list, int space) * N == 1.4 (1 + 4/threshold) */ -/* Divide by 10, so that the default incremental threshold of 10 - * scans objects at 1% of the heap size */ -#define SCAN_RATE_DIVISOR 10 +/* Multiply by 4 so that the default incremental threshold of 10 + * scans objects at 20% the rate of object creation */ +#define SCAN_RATE_MULTIPLIER 2 static void add_stats(GCState *gcstate, int gen, struct gc_collection_stats *stats) @@ -1312,7 +1311,7 @@ gc_collect_young(PyThreadState *tstate, if (scale_factor < 1) { scale_factor = 1; } - gcstate->work_to_do += gcstate->heap_size / SCAN_RATE_DIVISOR / scale_factor; + gcstate->work_to_do += survivor_count + survivor_count * SCAN_RATE_MULTIPLIER / scale_factor; add_stats(gcstate, 0, stats); } @@ -1383,12 +1382,12 @@ expand_region_transitively_reachable(PyGC_Head *container, PyGC_Head *gc, GCStat static void completed_cycle(GCState *gcstate) { -#ifdef Py_DEBUG PyGC_Head *not_visited = &gcstate->old[gcstate->visited_space^1].head; assert(gc_list_is_empty(not_visited)); -#endif gcstate->visited_space = flip_old_space(gcstate->visited_space); - gcstate->work_to_do = 0; + if (gcstate->work_to_do > 0) { + gcstate->work_to_do = 0; + } } static void @@ -1403,13 +1402,13 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats) if (scale_factor < 1) { scale_factor = 1; } + Py_ssize_t increment_size = 0; gc_list_merge(&gcstate->young.head, &increment); gcstate->young.count = 0; if (gcstate->visited_space) { /* objects in visited space have bit set, so we set it here */ gc_list_set_space(&increment, 1); } - Py_ssize_t increment_size = 0; while (increment_size < gcstate->work_to_do) { if (gc_list_is_empty(not_visited)) { break; @@ -1423,11 +1422,14 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats) PyGC_Head survivors; gc_list_init(&survivors); gc_collect_region(tstate, &increment, &survivors, UNTRACK_TUPLES, stats); + Py_ssize_t survivor_count = gc_list_size(&survivors); gc_list_merge(&survivors, visited); assert(gc_list_is_empty(&increment)); - gcstate->work_to_do += gcstate->heap_size / SCAN_RATE_DIVISOR / scale_factor; + gcstate->work_to_do += survivor_count + survivor_count * SCAN_RATE_MULTIPLIER / scale_factor; gcstate->work_to_do -= increment_size; - + if (gcstate->work_to_do < 0) { + gcstate->work_to_do = 0; + } validate_old(gcstate); add_stats(gcstate, 1, stats); if (gc_list_is_empty(not_visited)) { @@ -1673,7 +1675,7 @@ _PyGC_GetReferrers(PyInterpreterState *interp, PyObject *objs) } PyObject * -_PyGC_GetObjects(PyInterpreterState *interp, int generation) +_PyGC_GetObjects(PyInterpreterState *interp, Py_ssize_t generation) { assert(generation >= -1 && generation < NUM_GENERATIONS); GCState *gcstate = &interp->gc; @@ -1992,7 +1994,6 @@ _PyObject_GC_Link(PyObject *op) gc->_gc_next = 0; gc->_gc_prev = 0; gcstate->young.count++; /* number of allocated GC objects */ - gcstate->heap_size++; if (gcstate->young.count > gcstate->young.threshold && gcstate->enabled && gcstate->young.threshold && @@ -2119,7 +2120,6 @@ PyObject_GC_Del(void *op) if (gcstate->young.count > 0) { gcstate->young.count--; } - gcstate->heap_size--; PyObject_Free(((char *)op)-presize); } diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 8a20c4b233257a..2d653617d37080 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -1377,7 +1377,7 @@ visit_get_objects(const mi_heap_t *heap, const mi_heap_area_t *area, } PyObject * -_PyGC_GetObjects(PyInterpreterState *interp, int generation) +_PyGC_GetObjects(PyInterpreterState *interp, Py_ssize_t generation) { PyObject *result = PyList_New(0); if (!result) { From d15eace1d6165a63810dd9cca7ec502a43a699f5 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sun, 29 Sep 2024 21:00:31 +0200 Subject: [PATCH 07/15] Revert "GH-108362: Incremental Cycle GC (GH-116206)" This reverts commit 15309329b65a285cb7b3071f0f08ac964b61411b. --- Doc/whatsnew/3.13.rst | 26 + Include/internal/pycore_gc.h | 40 +- Include/internal/pycore_object.h | 3 +- Include/internal/pycore_runtime_init.h | 8 +- Lib/test/test_gc.py | 85 +-- Modules/gcmodule.c | 25 +- Python/gc.c | 813 ++++++++++--------------- Python/gc_free_threading.c | 21 +- Python/optimizer.c | 2 +- Tools/gdb/libpython.py | 7 +- 10 files changed, 405 insertions(+), 625 deletions(-) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 05d99064db2486..5a7ba886749a9b 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -908,6 +908,7 @@ fractions (Contributed by Mark Dickinson in :gh:`111320`.) +<<<<<<< HEAD gc -- @@ -938,6 +939,31 @@ may not work exactly as intended, but it is very unlikely to be harmful. All other code will work just fine. +||||||| 15309329b65 (GH-108362: Incremental Cycle GC (GH-116206)) +gc +-- + +* The cyclic garbage collector is now incremental, which changes the meanings + of the results of :meth:`gc.get_threshold` and :meth:`gc.get_threshold` as + well as :meth:`gc.get_count` and :meth:`gc.get_stats`. +* :meth:`gc.get_threshold` returns a three-tuple for backwards compatibility, + the first value is the threshold for young collections, as before, the second + value determines the rate at which the old collection is scanned; the + default is 10 and higher values mean that the old collection is scanned more slowly. + The third value is meangless and is always zero. +* :meth:`gc.set_threshold` ignores any items after the second. +* :meth:`gc.get_count` and :meth:`gc.get_stats`. + These functions return the same format of results as before. + The only difference is that instead of the results refering to + the young, aging and old generations, the results refer to the + young generation and the aging and collecting spaces of the old generation. + +In summary, code that attempted to manipulate the behavior of the cycle GC may +not work exactly as intended, but it is very unlikely to harmful. +All other code will work just fine. + +======= +>>>>>>> parent of 15309329b65 (GH-108362: Incremental Cycle GC (GH-116206)) glob ---- diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index 2d8ed5ddcb4383..b2df172f65dd64 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -142,14 +142,11 @@ static inline void _PyObject_GC_SET_SHARED_INLINE(PyObject *op) { /* Bit flags for _gc_prev */ /* Bit 0 is set when tp_finalize is called */ -#define _PyGC_PREV_MASK_FINALIZED 1 +#define _PyGC_PREV_MASK_FINALIZED (1) /* Bit 1 is set when the object is in generation which is GCed currently. */ -#define _PyGC_PREV_MASK_COLLECTING 2 - -/* Bit 0 is set if the object belongs to old space 1 */ -#define _PyGC_NEXT_MASK_OLD_SPACE_1 1 - -#define _PyGC_PREV_SHIFT 2 +#define _PyGC_PREV_MASK_COLLECTING (2) +/* The (N-2) most significant bits contain the real address. */ +#define _PyGC_PREV_SHIFT (2) #define _PyGC_PREV_MASK (((uintptr_t) -1) << _PyGC_PREV_SHIFT) /* set for debugging information */ @@ -175,13 +172,11 @@ typedef enum { // Lowest bit of _gc_next is used for flags only in GC. // But it is always 0 for normal code. static inline PyGC_Head* _PyGCHead_NEXT(PyGC_Head *gc) { - uintptr_t next = gc->_gc_next & _PyGC_PREV_MASK; + uintptr_t next = gc->_gc_next; return (PyGC_Head*)next; } static inline void _PyGCHead_SET_NEXT(PyGC_Head *gc, PyGC_Head *next) { - uintptr_t unext = (uintptr_t)next; - assert((unext & ~_PyGC_PREV_MASK) == 0); - gc->_gc_next = (gc->_gc_next & ~_PyGC_PREV_MASK) | unext; + gc->_gc_next = (uintptr_t)next; } // Lowest two bits of _gc_prev is used for _PyGC_PREV_MASK_* flags. @@ -189,7 +184,6 @@ static inline PyGC_Head* _PyGCHead_PREV(PyGC_Head *gc) { uintptr_t prev = (gc->_gc_prev & _PyGC_PREV_MASK); return (PyGC_Head*)prev; } - static inline void _PyGCHead_SET_PREV(PyGC_Head *gc, PyGC_Head *prev) { uintptr_t uprev = (uintptr_t)prev; assert((uprev & ~_PyGC_PREV_MASK) == 0); @@ -275,13 +269,6 @@ struct gc_generation { generations */ }; -struct gc_collection_stats { - /* number of collected objects */ - Py_ssize_t collected; - /* total number of uncollectable objects (put into gc.garbage) */ - Py_ssize_t uncollectable; -}; - /* Running stats per generation */ struct gc_generation_stats { /* total number of collections */ @@ -303,8 +290,8 @@ struct _gc_runtime_state { int enabled; int debug; /* linked lists of container objects */ - struct gc_generation young; - struct gc_generation old[2]; + struct gc_generation generations[NUM_GENERATIONS]; + PyGC_Head *generation0; /* a permanent generation which won't be collected */ struct gc_generation permanent_generation; struct gc_generation_stats generation_stats[NUM_GENERATIONS]; @@ -315,11 +302,7 @@ struct _gc_runtime_state { /* a list of callbacks to be invoked when collection is performed */ PyObject *callbacks; - Py_ssize_t work_to_do; - /* Which of the old spaces is the visited space */ - int visited_space; - -#ifdef Py_GIL_DISABLED +#ifndef Py_GIL_DISABLED /* This is the number of objects that survived the last full collection. It approximates the number of long lived objects tracked by the GC. @@ -352,7 +335,8 @@ struct _gc_thread_state { extern void _PyGC_InitState(struct _gc_runtime_state *); -extern Py_ssize_t _PyGC_Collect(PyThreadState *tstate, int generation, _PyGC_Reason reason); +extern Py_ssize_t _PyGC_Collect(PyThreadState *tstate, int generation, + _PyGC_Reason reason); extern void _PyGC_CollectNoFail(PyThreadState *tstate); /* Freeze objects tracked by the GC and ignore them in future collections. */ @@ -362,7 +346,7 @@ extern void _PyGC_Unfreeze(PyInterpreterState *interp); /* Number of frozen objects */ extern Py_ssize_t _PyGC_GetFreezeCount(PyInterpreterState *interp); -extern PyObject *_PyGC_GetObjects(PyInterpreterState *interp, Py_ssize_t generation); +extern PyObject *_PyGC_GetObjects(PyInterpreterState *interp, int generation); extern PyObject *_PyGC_GetReferrers(PyInterpreterState *interp, PyObject *objs); // Functions to clear types free lists diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 7862cb1bafa9d4..39f5600f7ae907 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -353,12 +353,11 @@ static inline void _PyObject_GC_TRACK( filename, lineno, __func__); PyInterpreterState *interp = _PyInterpreterState_GET(); - PyGC_Head *generation0 = &interp->gc.young.head; + PyGC_Head *generation0 = interp->gc.generation0; PyGC_Head *last = (PyGC_Head*)(generation0->_gc_prev); _PyGCHead_SET_NEXT(last, gc); _PyGCHead_SET_PREV(gc, last); _PyGCHead_SET_NEXT(gc, generation0); - assert((gc->_gc_next & _PyGC_NEXT_MASK_OLD_SPACE_1) == 0); generation0->_gc_prev = (uintptr_t)gc; #endif } diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index e6adb98eb19130..7eef9edc0aa33d 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -228,12 +228,12 @@ extern PyTypeObject _PyExc_MemoryError; }, \ .gc = { \ .enabled = 1, \ - .young = { .threshold = 2000, }, \ - .old = { \ + .generations = { \ + /* .head is set in _PyGC_InitState(). */ \ + { .threshold = 2000, }, \ + { .threshold = 10, }, \ { .threshold = 10, }, \ - { .threshold = 0, }, \ }, \ - .work_to_do = -5000, \ }, \ .qsbr = { \ .wr_seq = QSBR_INITIAL, \ diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index 65742612310f80..c285fbddb1bf55 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -392,11 +392,19 @@ def test_collect_generations(self): # each call to collect(N) x = [] gc.collect(0) - # x is now in the old gen + # x is now in gen 1 a, b, c = gc.get_count() - # We don't check a since its exact values depends on + gc.collect(1) + # x is now in gen 2 + d, e, f = gc.get_count() + gc.collect(2) + # x is now in gen 3 + g, h, i = gc.get_count() + # We don't check a, d, g since their exact values depends on # internal implementation details of the interpreter. self.assertEqual((b, c), (1, 0)) + self.assertEqual((e, f), (0, 1)) + self.assertEqual((h, i), (0, 0)) def test_trashcan(self): class Ouch: @@ -851,6 +859,16 @@ def test_get_objects_generations(self): self.assertFalse( any(l is element for element in gc.get_objects(generation=2)) ) + gc.collect(generation=1) + self.assertFalse( + any(l is element for element in gc.get_objects(generation=0)) + ) + self.assertFalse( + any(l is element for element in gc.get_objects(generation=1)) + ) + self.assertTrue( + any(l is element for element in gc.get_objects(generation=2)) + ) gc.collect(generation=2) self.assertFalse( any(l is element for element in gc.get_objects(generation=0)) @@ -1088,69 +1106,6 @@ def test_get_referents_on_capsule(self): gc.get_referents(tracked_capsule) - -class IncrementalGCTests(unittest.TestCase): - - def setUp(self): - # Reenable GC as it is disabled module-wide - gc.enable() - - def tearDown(self): - gc.disable() - - @requires_gil_enabled("Free threading does not support incremental GC") - # Use small increments to emulate longer running process in a shorter time - @gc_threshold(200, 10) - def test_incremental_gc_handles_fast_cycle_creation(self): - - class LinkedList: - - #Use slots to reduce number of implicit objects - __slots__ = "next", "prev", "surprise" - - def __init__(self, next=None, prev=None): - self.next = next - if next is not None: - next.prev = self - self.prev = prev - if prev is not None: - prev.next = self - - def make_ll(depth): - head = LinkedList() - for i in range(depth): - head = LinkedList(head, head.prev) - return head - - head = make_ll(10000) - count = 10000 - - # We expect the counts to go negative eventually - # as there will some objects we aren't counting, - # e.g. the gc stats dicts. The test merely checks - # that the counts don't grow. - - enabled = gc.isenabled() - gc.enable() - olds = [] - for i in range(1000): - newhead = make_ll(200) - count += 200 - newhead.surprise = head - olds.append(newhead) - if len(olds) == 50: - stats = gc.get_stats() - young = stats[0] - incremental = stats[1] - old = stats[2] - collected = young['collected'] + incremental['collected'] + old['collected'] - live = count - collected - self.assertLess(live, 25000) - del olds[:] - if not enabled: - gc.disable() - - class GCCallbackTests(unittest.TestCase): def setUp(self): # Save gc state and disable it. diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index fe609286efcdf4..3235ad67c70d40 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -158,12 +158,17 @@ gc_set_threshold_impl(PyObject *module, int threshold0, int group_right_1, { GCState *gcstate = get_gc_state(); - gcstate->young.threshold = threshold0; + gcstate->generations[0].threshold = threshold0; if (group_right_1) { - gcstate->old[0].threshold = threshold1; + gcstate->generations[1].threshold = threshold1; } if (group_right_2) { - gcstate->old[1].threshold = threshold2; + gcstate->generations[2].threshold = threshold2; + + /* generations higher than 2 get the same threshold */ + for (int i = 3; i < NUM_GENERATIONS; i++) { + gcstate->generations[i].threshold = gcstate->generations[2].threshold; + } } Py_RETURN_NONE; } @@ -180,9 +185,9 @@ gc_get_threshold_impl(PyObject *module) { GCState *gcstate = get_gc_state(); return Py_BuildValue("(iii)", - gcstate->young.threshold, - gcstate->old[0].threshold, - 0); + gcstate->generations[0].threshold, + gcstate->generations[1].threshold, + gcstate->generations[2].threshold); } /*[clinic input] @@ -202,14 +207,14 @@ gc_get_count_impl(PyObject *module) struct _gc_thread_state *gc = &tstate->gc; // Flush the local allocation count to the global count - _Py_atomic_add_int(&gcstate->young.count, (int)gc->alloc_count); + _Py_atomic_add_int(&gcstate->generations[0].count, (int)gc->alloc_count); gc->alloc_count = 0; #endif return Py_BuildValue("(iii)", - gcstate->young.count, - gcstate->old[gcstate->visited_space].count, - gcstate->old[gcstate->visited_space^1].count); + gcstate->generations[0].count, + gcstate->generations[1].count, + gcstate->generations[2].count); } /*[clinic input] diff --git a/Python/gc.c b/Python/gc.c index 27c23f5b019c8e..8dbcb340d4089d 100644 --- a/Python/gc.c +++ b/Python/gc.c @@ -45,7 +45,7 @@ typedef struct _gc_runtime_state GCState; // move_legacy_finalizers() removes this flag instead. // Between them, unreachable list is not normal list and we can not use // most gc_list_* functions for it. -#define NEXT_MASK_UNREACHABLE 2 +#define NEXT_MASK_UNREACHABLE (1) #define AS_GC(op) _Py_AS_GC(op) #define FROM_GC(gc) _Py_FROM_GC(gc) @@ -95,48 +95,9 @@ gc_decref(PyGC_Head *g) g->_gc_prev -= 1 << _PyGC_PREV_SHIFT; } -static inline int -gc_old_space(PyGC_Head *g) -{ - return g->_gc_next & _PyGC_NEXT_MASK_OLD_SPACE_1; -} -static inline int -flip_old_space(int space) -{ - assert(space == 0 || space == 1); - return space ^ _PyGC_NEXT_MASK_OLD_SPACE_1; -} +#define GEN_HEAD(gcstate, n) (&(gcstate)->generations[n].head) -static inline void -gc_flip_old_space(PyGC_Head *g) -{ - g->_gc_next ^= _PyGC_NEXT_MASK_OLD_SPACE_1; -} - -static inline void -gc_set_old_space(PyGC_Head *g, int space) -{ - assert(space == 0 || space == _PyGC_NEXT_MASK_OLD_SPACE_1); - g->_gc_next &= ~_PyGC_NEXT_MASK_OLD_SPACE_1; - g->_gc_next |= space; -} - -static PyGC_Head * -GEN_HEAD(GCState *gcstate, int n) -{ - assert((gcstate->visited_space & (~1)) == 0); - switch(n) { - case 0: - return &gcstate->young.head; - case 1: - return &gcstate->old[gcstate->visited_space].head; - case 2: - return &gcstate->old[gcstate->visited_space^1].head; - default: - Py_UNREACHABLE(); - } -} static GCState * get_gc_state(void) @@ -155,12 +116,11 @@ _PyGC_InitState(GCState *gcstate) GEN.head._gc_prev = (uintptr_t)&GEN.head; \ } while (0) - assert(gcstate->young.count == 0); - assert(gcstate->old[0].count == 0); - assert(gcstate->old[1].count == 0); - INIT_HEAD(gcstate->young); - INIT_HEAD(gcstate->old[0]); - INIT_HEAD(gcstate->old[1]); + for (int i = 0; i < NUM_GENERATIONS; i++) { + assert(gcstate->generations[i].count == 0); + INIT_HEAD(gcstate->generations[i]); + }; + gcstate->generation0 = GEN_HEAD(gcstate, 0); INIT_HEAD(gcstate->permanent_generation); #undef INIT_HEAD @@ -258,7 +218,6 @@ gc_list_is_empty(PyGC_Head *list) static inline void gc_list_append(PyGC_Head *node, PyGC_Head *list) { - assert((list->_gc_prev & ~_PyGC_PREV_MASK) == 0); PyGC_Head *last = (PyGC_Head *)list->_gc_prev; // last <-> node @@ -316,8 +275,6 @@ gc_list_merge(PyGC_Head *from, PyGC_Head *to) PyGC_Head *from_tail = GC_PREV(from); assert(from_head != from); assert(from_tail != from); - assert(gc_list_is_empty(to) || - gc_old_space(to_tail) == gc_old_space(from_tail)); _PyGCHead_SET_NEXT(to_tail, from_head); _PyGCHead_SET_PREV(from_head, to_tail); @@ -386,8 +343,8 @@ enum flagstates {collecting_clear_unreachable_clear, static void validate_list(PyGC_Head *head, enum flagstates flags) { - assert((head->_gc_prev & ~_PyGC_PREV_MASK) == 0); - assert((head->_gc_next & ~_PyGC_PREV_MASK) == 0); + assert((head->_gc_prev & PREV_MASK_COLLECTING) == 0); + assert((head->_gc_next & NEXT_MASK_UNREACHABLE) == 0); uintptr_t prev_value = 0, next_value = 0; switch (flags) { case collecting_clear_unreachable_clear: @@ -409,7 +366,7 @@ validate_list(PyGC_Head *head, enum flagstates flags) PyGC_Head *gc = GC_NEXT(head); while (gc != head) { PyGC_Head *trueprev = GC_PREV(gc); - PyGC_Head *truenext = GC_NEXT(gc); + PyGC_Head *truenext = (PyGC_Head *)(gc->_gc_next & ~NEXT_MASK_UNREACHABLE); assert(truenext != NULL); assert(trueprev == prev); assert((gc->_gc_prev & PREV_MASK_COLLECTING) == prev_value); @@ -419,44 +376,8 @@ validate_list(PyGC_Head *head, enum flagstates flags) } assert(prev == GC_PREV(head)); } - -static void -validate_old(GCState *gcstate) -{ - for (int space = 0; space < 2; space++) { - PyGC_Head *head = &gcstate->old[space].head; - PyGC_Head *gc = GC_NEXT(head); - while (gc != head) { - PyGC_Head *next = GC_NEXT(gc); - assert(gc_old_space(gc) == space); - gc = next; - } - } -} - -static void -validate_consistent_old_space(PyGC_Head *head) -{ - PyGC_Head *prev = head; - PyGC_Head *gc = GC_NEXT(head); - if (gc == head) { - return; - } - int old_space = gc_old_space(gc); - while (gc != head) { - PyGC_Head *truenext = GC_NEXT(gc); - assert(truenext != NULL); - assert(gc_old_space(gc) == old_space); - prev = gc; - gc = truenext; - } - assert(prev == GC_PREV(head)); -} - #else #define validate_list(x, y) do{}while(0) -#define validate_old(g) do{}while(0) -#define validate_consistent_old_space(l) do{}while(0) #endif /*** end of list stuff ***/ @@ -474,6 +395,10 @@ update_refs(PyGC_Head *containers) while (gc != containers) { next = GC_NEXT(gc); PyObject *op = FROM_GC(gc); + /* Move any object that might have become immortal to the + * permanent generation as the reference count is not accurately + * reflecting the actual number of live references to this object + */ if (_Py_IsImmortal(op)) { gc_list_move(gc, &get_gc_state()->permanent_generation.head); gc = next; @@ -576,13 +501,12 @@ visit_reachable(PyObject *op, void *arg) // Manually unlink gc from unreachable list because the list functions // don't work right in the presence of NEXT_MASK_UNREACHABLE flags. PyGC_Head *prev = GC_PREV(gc); - PyGC_Head *next = GC_NEXT(gc); + PyGC_Head *next = (PyGC_Head*)(gc->_gc_next & ~NEXT_MASK_UNREACHABLE); _PyObject_ASSERT(FROM_GC(prev), prev->_gc_next & NEXT_MASK_UNREACHABLE); _PyObject_ASSERT(FROM_GC(next), next->_gc_next & NEXT_MASK_UNREACHABLE); - prev->_gc_next = gc->_gc_next; // copy flag bits - gc->_gc_next &= ~NEXT_MASK_UNREACHABLE; + prev->_gc_next = gc->_gc_next; // copy NEXT_MASK_UNREACHABLE _PyGCHead_SET_PREV(next, prev); gc_list_append(gc, reachable); @@ -634,9 +558,6 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable) * or to the right have been scanned yet. */ - validate_consistent_old_space(young); - /* Record which old space we are in, and set NEXT_MASK_UNREACHABLE bit for convenience */ - uintptr_t flags = NEXT_MASK_UNREACHABLE | (gc->_gc_next & _PyGC_NEXT_MASK_OLD_SPACE_1); while (gc != young) { if (gc_get_refs(gc)) { /* gc is definitely reachable from outside the @@ -682,18 +603,17 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable) // But this may pollute the unreachable list head's 'next' pointer // too. That's semantically senseless but expedient here - the // damage is repaired when this function ends. - last->_gc_next = flags | (uintptr_t)gc; + last->_gc_next = (NEXT_MASK_UNREACHABLE | (uintptr_t)gc); _PyGCHead_SET_PREV(gc, last); - gc->_gc_next = flags | (uintptr_t)unreachable; + gc->_gc_next = (NEXT_MASK_UNREACHABLE | (uintptr_t)unreachable); unreachable->_gc_prev = (uintptr_t)gc; } - gc = _PyGCHead_NEXT(prev); + gc = (PyGC_Head*)prev->_gc_next; } // young->_gc_prev must be last element remained in the list. young->_gc_prev = (uintptr_t)prev; - young->_gc_next &= _PyGC_PREV_MASK; // don't let the pollution of the list head's next pointer leak - unreachable->_gc_next &= _PyGC_PREV_MASK; + unreachable->_gc_next &= ~NEXT_MASK_UNREACHABLE; } static void @@ -752,8 +672,8 @@ move_legacy_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers) PyObject *op = FROM_GC(gc); _PyObject_ASSERT(op, gc->_gc_next & NEXT_MASK_UNREACHABLE); - next = GC_NEXT(gc); gc->_gc_next &= ~NEXT_MASK_UNREACHABLE; + next = (PyGC_Head*)gc->_gc_next; if (has_legacy_finalizer(op)) { gc_clear_collecting(gc); @@ -776,8 +696,8 @@ clear_unreachable_mask(PyGC_Head *unreachable) PyGC_Head *gc, *next; for (gc = GC_NEXT(unreachable); gc != unreachable; gc = next) { _PyObject_ASSERT((PyObject*)FROM_GC(gc), gc->_gc_next & NEXT_MASK_UNREACHABLE); - next = GC_NEXT(gc); gc->_gc_next &= ~NEXT_MASK_UNREACHABLE; + next = (PyGC_Head*)gc->_gc_next; } validate_list(unreachable, collecting_set_unreachable_clear); } @@ -1109,6 +1029,25 @@ delete_garbage(PyThreadState *tstate, GCState *gcstate, } +// Show stats for objects in each generations +static void +show_stats_each_generations(GCState *gcstate) +{ + char buf[100]; + size_t pos = 0; + + for (int i = 0; i < NUM_GENERATIONS && pos < sizeof(buf); i++) { + pos += PyOS_snprintf(buf+pos, sizeof(buf)-pos, + " %zd", + gc_list_size(GEN_HEAD(gcstate, i))); + } + + PySys_FormatStderr( + "gc: objects in each generation:%s\n" + "gc: objects in permanent generation: %zd\n", + buf, gc_list_size(&gcstate->permanent_generation.head)); +} + /* Deduce which objects among "base" are unreachable from outside the list and move them to 'unreachable'. The process consist in the following steps: @@ -1182,6 +1121,7 @@ deduce_unreachable(PyGC_Head *base, PyGC_Head *unreachable) { * the reachable objects instead. But this is a one-time cost, probably not * worth complicating the code to speed just a little. */ + gc_list_init(unreachable); move_unreachable(base, unreachable); // gc_prev is pointer again validate_list(base, collecting_clear_unreachable_clear); validate_list(unreachable, collecting_set_unreachable_set); @@ -1220,290 +1160,220 @@ handle_resurrected_objects(PyGC_Head *unreachable, PyGC_Head* still_unreachable, } -#define UNTRACK_TUPLES 1 -#define UNTRACK_DICTS 2 - -static void -gc_collect_region(PyThreadState *tstate, - PyGC_Head *from, - PyGC_Head *to, - int untrack, - struct gc_collection_stats *stats); - -static inline Py_ssize_t -gc_list_set_space(PyGC_Head *list, uintptr_t space) -{ - Py_ssize_t size = 0; - PyGC_Head *gc; - for (gc = GC_NEXT(list); gc != list; gc = GC_NEXT(gc)) { - gc_set_old_space(gc, space); - size++; - } - return size; -} - -/* Making progress in the incremental collector - * In order to eventually collect all cycles - * the incremental collector must progress through the old - * space faster than objects are added to the old space. - * - * Each young or incremental collection adds a numebr of - * objects, S (for survivors) to the old space, and - * incremental collectors scan I objects from the old space. - * I > S must be true. We also want I > S * N to be where - * N > 1. Higher values of N mean that the old space is - * scanned more rapidly. - * The default incremental threshold of 10 translates to - * N == 1.4 (1 + 4/threshold) +/* Invoke progress callbacks to notify clients that garbage collection + * is starting or stopping */ - -/* Multiply by 4 so that the default incremental threshold of 10 - * scans objects at 20% the rate of object creation */ -#define SCAN_RATE_MULTIPLIER 2 - static void -add_stats(GCState *gcstate, int gen, struct gc_collection_stats *stats) +invoke_gc_callback(PyThreadState *tstate, const char *phase, + int generation, Py_ssize_t collected, + Py_ssize_t uncollectable) { - gcstate->generation_stats[gen].collected += stats->collected; - gcstate->generation_stats[gen].uncollectable += stats->uncollectable; - gcstate->generation_stats[gen].collections += 1; -} + assert(!_PyErr_Occurred(tstate)); -static void -gc_collect_young(PyThreadState *tstate, - struct gc_collection_stats *stats) -{ + /* we may get called very early */ GCState *gcstate = &tstate->interp->gc; - PyGC_Head *young = &gcstate->young.head; - PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head; -#ifdef Py_STATS - { - Py_ssize_t count = 0; - PyGC_Head *gc; - for (gc = GC_NEXT(young); gc != young; gc = GC_NEXT(gc)) { - count++; - } + if (gcstate->callbacks == NULL) { + return; } -#endif - PyGC_Head survivors; - gc_list_init(&survivors); - gc_collect_region(tstate, young, &survivors, UNTRACK_TUPLES, stats); - Py_ssize_t survivor_count = 0; - if (gcstate->visited_space) { - /* objects in visited space have bit set, so we set it here */ - survivor_count = gc_list_set_space(&survivors, 1); - } - else { - PyGC_Head *gc; - for (gc = GC_NEXT(&survivors); gc != &survivors; gc = GC_NEXT(gc)) { -#ifdef GC_DEBUG - assert(gc_old_space(gc) == 0); -#endif - survivor_count++; + /* The local variable cannot be rebound, check it for sanity */ + assert(PyList_CheckExact(gcstate->callbacks)); + PyObject *info = NULL; + if (PyList_GET_SIZE(gcstate->callbacks) != 0) { + info = Py_BuildValue("{sisnsn}", + "generation", generation, + "collected", collected, + "uncollectable", uncollectable); + if (info == NULL) { + PyErr_FormatUnraisable("Exception ignored on invoking gc callbacks"); + return; } } - gc_list_merge(&survivors, visited); - validate_old(gcstate); - gcstate->young.count = 0; - gcstate->old[gcstate->visited_space].count++; - Py_ssize_t scale_factor = gcstate->old[0].threshold; - if (scale_factor < 1) { - scale_factor = 1; + + PyObject *phase_obj = PyUnicode_FromString(phase); + if (phase_obj == NULL) { + Py_XDECREF(info); + PyErr_FormatUnraisable("Exception ignored on invoking gc callbacks"); + return; } - gcstate->work_to_do += survivor_count + survivor_count * SCAN_RATE_MULTIPLIER / scale_factor; - add_stats(gcstate, 0, stats); -} -static inline int -IS_IN_VISITED(PyGC_Head *gc, int visited_space) -{ - assert(visited_space == 0 || flip_old_space(visited_space) == 0); - return gc_old_space(gc) == visited_space; + PyObject *stack[] = {phase_obj, info}; + for (Py_ssize_t i=0; icallbacks); i++) { + PyObject *r, *cb = PyList_GET_ITEM(gcstate->callbacks, i); + Py_INCREF(cb); /* make sure cb doesn't go away */ + r = PyObject_Vectorcall(cb, stack, 2, NULL); + if (r == NULL) { + PyErr_WriteUnraisable(cb); + } + else { + Py_DECREF(r); + } + Py_DECREF(cb); + } + Py_DECREF(phase_obj); + Py_XDECREF(info); + assert(!_PyErr_Occurred(tstate)); } -struct container_and_flag { - PyGC_Head *container; - int visited_space; - uintptr_t size; -}; -/* A traversal callback for adding to container) */ +/* Find the oldest generation (highest numbered) where the count + * exceeds the threshold. Objects in the that generation and + * generations younger than it will be collected. */ static int -visit_add_to_container(PyObject *op, void *arg) -{ - OBJECT_STAT_INC(object_visits); - struct container_and_flag *cf = (struct container_and_flag *)arg; - int visited = cf->visited_space; - assert(visited == get_gc_state()->visited_space); - if (!_Py_IsImmortal(op) && _PyObject_IS_GC(op)) { - PyGC_Head *gc = AS_GC(op); - if (_PyObject_GC_IS_TRACKED(op) && - gc_old_space(gc) != visited) { - gc_flip_old_space(gc); - gc_list_move(gc, cf->container); - cf->size++; +gc_select_generation(GCState *gcstate) +{ + for (int i = NUM_GENERATIONS-1; i >= 0; i--) { + if (gcstate->generations[i].count > gcstate->generations[i].threshold) { + /* Avoid quadratic performance degradation in number + of tracked objects (see also issue #4074): + + To limit the cost of garbage collection, there are two strategies; + - make each collection faster, e.g. by scanning fewer objects + - do less collections + This heuristic is about the latter strategy. + + In addition to the various configurable thresholds, we only trigger a + full collection if the ratio + + long_lived_pending / long_lived_total + + is above a given value (hardwired to 25%). + + The reason is that, while "non-full" collections (i.e., collections of + the young and middle generations) will always examine roughly the same + number of objects -- determined by the aforementioned thresholds --, + the cost of a full collection is proportional to the total number of + long-lived objects, which is virtually unbounded. + + Indeed, it has been remarked that doing a full collection every + of object creations entails a dramatic performance + degradation in workloads which consist in creating and storing lots of + long-lived objects (e.g. building a large list of GC-tracked objects would + show quadratic performance, instead of linear as expected: see issue #4074). + + Using the above ratio, instead, yields amortized linear performance in + the total number of objects (the effect of which can be summarized + thusly: "each full garbage collection is more and more costly as the + number of objects grows, but we do fewer and fewer of them"). + + This heuristic was suggested by Martin von Löwis on python-dev in + June 2008. His original analysis and proposal can be found at: + http://mail.python.org/pipermail/python-dev/2008-June/080579.html + */ + if (i == NUM_GENERATIONS - 1 + && gcstate->long_lived_pending < gcstate->long_lived_total / 4) + { + continue; + } + return i; } } - return 0; + return -1; } -static uintptr_t -expand_region_transitively_reachable(PyGC_Head *container, PyGC_Head *gc, GCState *gcstate) -{ - validate_list(container, collecting_clear_unreachable_clear); - struct container_and_flag arg = { - .container = container, - .visited_space = gcstate->visited_space, - .size = 0 - }; - assert(GC_NEXT(gc) == container); - while (gc != container) { - /* Survivors will be moved to visited space, so they should - * have been marked as visited */ - assert(IS_IN_VISITED(gc, gcstate->visited_space)); - PyObject *op = FROM_GC(gc); - if (_Py_IsImmortal(op)) { - PyGC_Head *next = GC_NEXT(gc); - gc_list_move(gc, &get_gc_state()->permanent_generation.head); - gc = next; - continue; - } - traverseproc traverse = Py_TYPE(op)->tp_traverse; - (void) traverse(op, - visit_add_to_container, - &arg); - gc = GC_NEXT(gc); - } - return arg.size; -} -/* Do bookkeeping for a completed GC cycle */ -static void -completed_cycle(GCState *gcstate) +/* This is the main function. Read this to understand how the + * collection process works. */ +static Py_ssize_t +gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) { - PyGC_Head *not_visited = &gcstate->old[gcstate->visited_space^1].head; - assert(gc_list_is_empty(not_visited)); - gcstate->visited_space = flip_old_space(gcstate->visited_space); - if (gcstate->work_to_do > 0) { - gcstate->work_to_do = 0; + int i; + Py_ssize_t m = 0; /* # objects collected */ + Py_ssize_t n = 0; /* # unreachable objects that couldn't be collected */ + PyGC_Head *young; /* the generation we are examining */ + PyGC_Head *old; /* next older generation */ + PyGC_Head unreachable; /* non-problematic unreachable trash */ + PyGC_Head finalizers; /* objects with, & reachable from, __del__ */ + PyGC_Head *gc; + PyTime_t t1 = 0; /* initialize to prevent a compiler warning */ + GCState *gcstate = &tstate->interp->gc; + + // gc_collect_main() must not be called before _PyGC_Init + // or after _PyGC_Fini() + assert(gcstate->garbage != NULL); + assert(!_PyErr_Occurred(tstate)); + + int expected = 0; + if (!_Py_atomic_compare_exchange_int(&gcstate->collecting, &expected, 1)) { + // Don't start a garbage collection if one is already in progress. + return 0; } -} -static void -gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats) -{ - GCState *gcstate = &tstate->interp->gc; - PyGC_Head *not_visited = &gcstate->old[gcstate->visited_space^1].head; - PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head; - PyGC_Head increment; - gc_list_init(&increment); - Py_ssize_t scale_factor = gcstate->old[0].threshold; - if (scale_factor < 1) { - scale_factor = 1; - } - Py_ssize_t increment_size = 0; - gc_list_merge(&gcstate->young.head, &increment); - gcstate->young.count = 0; - if (gcstate->visited_space) { - /* objects in visited space have bit set, so we set it here */ - gc_list_set_space(&increment, 1); - } - while (increment_size < gcstate->work_to_do) { - if (gc_list_is_empty(not_visited)) { - break; + if (generation == GENERATION_AUTO) { + // Select the oldest generation that needs collecting. We will collect + // objects from that generation and all generations younger than it. + generation = gc_select_generation(gcstate); + if (generation < 0) { + // No generation needs to be collected. + _Py_atomic_store_int(&gcstate->collecting, 0); + return 0; } - PyGC_Head *gc = _PyGCHead_NEXT(not_visited); - gc_list_move(gc, &increment); - increment_size++; - gc_set_old_space(gc, gcstate->visited_space); - increment_size += expand_region_transitively_reachable(&increment, gc, gcstate); - } - PyGC_Head survivors; - gc_list_init(&survivors); - gc_collect_region(tstate, &increment, &survivors, UNTRACK_TUPLES, stats); - Py_ssize_t survivor_count = gc_list_size(&survivors); - gc_list_merge(&survivors, visited); - assert(gc_list_is_empty(&increment)); - gcstate->work_to_do += survivor_count + survivor_count * SCAN_RATE_MULTIPLIER / scale_factor; - gcstate->work_to_do -= increment_size; - if (gcstate->work_to_do < 0) { - gcstate->work_to_do = 0; - } - validate_old(gcstate); - add_stats(gcstate, 1, stats); - if (gc_list_is_empty(not_visited)) { - completed_cycle(gcstate); } -} + assert(generation >= 0 && generation < NUM_GENERATIONS); -static void -gc_collect_full(PyThreadState *tstate, - struct gc_collection_stats *stats) -{ - GCState *gcstate = &tstate->interp->gc; - validate_old(gcstate); - PyGC_Head *young = &gcstate->young.head; - PyGC_Head *old0 = &gcstate->old[0].head; - PyGC_Head *old1 = &gcstate->old[1].head; - /* merge all generations into old0 */ - gc_list_merge(young, old0); - gcstate->young.count = 0; - PyGC_Head *gc = GC_NEXT(old1); - while (gc != old1) { - PyGC_Head *next = GC_NEXT(gc); - gc_set_old_space(gc, 0); - gc = next; +#ifdef Py_STATS + if (_Py_stats) { + _Py_stats->object_stats.object_visits = 0; } - gc_list_merge(old1, old0); +#endif + GC_STAT_ADD(generation, collections, 1); - gc_collect_region(tstate, old0, old0, - UNTRACK_TUPLES | UNTRACK_DICTS, - stats); - gcstate->visited_space = 1; - gcstate->young.count = 0; - gcstate->old[0].count = 0; - gcstate->old[1].count = 0; + if (reason != _Py_GC_REASON_SHUTDOWN) { + invoke_gc_callback(tstate, "start", generation, 0, 0); + } - gcstate->work_to_do = - gcstate->young.threshold * 2; - _PyGC_ClearAllFreeLists(tstate->interp); - validate_old(gcstate); - add_stats(gcstate, 2, stats); -} + if (gcstate->debug & _PyGC_DEBUG_STATS) { + PySys_WriteStderr("gc: collecting generation %d...\n", generation); + show_stats_each_generations(gcstate); + // ignore error: don't interrupt the GC if reading the clock fails + (void)PyTime_PerfCounterRaw(&t1); + } -/* This is the main function. Read this to understand how the - * collection process works. */ -static void -gc_collect_region(PyThreadState *tstate, - PyGC_Head *from, - PyGC_Head *to, - int untrack, - struct gc_collection_stats *stats) -{ - PyGC_Head unreachable; /* non-problematic unreachable trash */ - PyGC_Head finalizers; /* objects with, & reachable from, __del__ */ - PyGC_Head *gc; /* initialize to prevent a compiler warning */ - GCState *gcstate = &tstate->interp->gc; + if (PyDTrace_GC_START_ENABLED()) { + PyDTrace_GC_START(generation); + } - assert(gcstate->garbage != NULL); - assert(!_PyErr_Occurred(tstate)); + /* update collection and allocation counters */ + if (generation+1 < NUM_GENERATIONS) { + gcstate->generations[generation+1].count += 1; + } + for (i = 0; i <= generation; i++) { + gcstate->generations[i].count = 0; + } - gc_list_init(&unreachable); - deduce_unreachable(from, &unreachable); - validate_consistent_old_space(from); - if (untrack & UNTRACK_TUPLES) { - untrack_tuples(from); + /* merge younger generations with one we are currently collecting */ + for (i = 0; i < generation; i++) { + gc_list_merge(GEN_HEAD(gcstate, i), GEN_HEAD(gcstate, generation)); } - if (untrack & UNTRACK_DICTS) { - untrack_dicts(from); + + /* handy references */ + young = GEN_HEAD(gcstate, generation); + if (generation < NUM_GENERATIONS-1) { + old = GEN_HEAD(gcstate, generation+1); } - validate_consistent_old_space(to); - if (from != to) { - gc_list_merge(from, to); + else { + old = young; } - validate_consistent_old_space(to); + validate_list(old, collecting_clear_unreachable_clear); + + deduce_unreachable(young, &unreachable); + + untrack_tuples(young); /* Move reachable objects to next generation. */ + if (young != old) { + if (generation == NUM_GENERATIONS - 2) { + gcstate->long_lived_pending += gc_list_size(young); + } + gc_list_merge(young, old); + } + else { + /* We only un-track dicts in full collections, to avoid quadratic + dict build-up. See issue #14775. */ + untrack_dicts(young); + gcstate->long_lived_pending = 0; + gcstate->long_lived_total = gc_list_size(young); + } /* All objects in unreachable are trash, but objects reachable from * legacy finalizers (e.g. tp_del) can't safely be deleted. @@ -1517,8 +1387,10 @@ gc_collect_region(PyThreadState *tstate, * and we move those into the finalizers list too. */ move_legacy_finalizer_reachable(&finalizers); + validate_list(&finalizers, collecting_clear_unreachable_clear); validate_list(&unreachable, collecting_set_unreachable_clear); + /* Print debugging information. */ if (gcstate->debug & _PyGC_DEBUG_COLLECTABLE) { for (gc = GC_NEXT(&unreachable); gc != &unreachable; gc = GC_NEXT(gc)) { @@ -1527,99 +1399,91 @@ gc_collect_region(PyThreadState *tstate, } /* Clear weakrefs and invoke callbacks as necessary. */ - stats->collected += handle_weakrefs(&unreachable, to); - validate_list(to, collecting_clear_unreachable_clear); + m += handle_weakrefs(&unreachable, old); + + validate_list(old, collecting_clear_unreachable_clear); validate_list(&unreachable, collecting_set_unreachable_clear); /* Call tp_finalize on objects which have one. */ finalize_garbage(tstate, &unreachable); + /* Handle any objects that may have resurrected after the call * to 'finalize_garbage' and continue the collection with the * objects that are still unreachable */ PyGC_Head final_unreachable; - gc_list_init(&final_unreachable); - handle_resurrected_objects(&unreachable, &final_unreachable, to); + handle_resurrected_objects(&unreachable, &final_unreachable, old); /* Call tp_clear on objects in the final_unreachable set. This will cause * the reference cycles to be broken. It may also cause some objects * in finalizers to be freed. */ - stats->collected += gc_list_size(&final_unreachable); - delete_garbage(tstate, gcstate, &final_unreachable, to); + m += gc_list_size(&final_unreachable); + delete_garbage(tstate, gcstate, &final_unreachable, old); /* Collect statistics on uncollectable objects found and print * debugging information. */ - Py_ssize_t n = 0; for (gc = GC_NEXT(&finalizers); gc != &finalizers; gc = GC_NEXT(gc)) { n++; - if (gcstate->debug & _PyGC_DEBUG_COLLECTABLE) + if (gcstate->debug & _PyGC_DEBUG_UNCOLLECTABLE) debug_cycle("uncollectable", FROM_GC(gc)); } - stats->uncollectable = n; + if (gcstate->debug & _PyGC_DEBUG_STATS) { + PyTime_t t2; + (void)PyTime_PerfCounterRaw(&t2); + double d = PyTime_AsSecondsDouble(t2 - t1); + PySys_WriteStderr( + "gc: done, %zd unreachable, %zd uncollectable, %.4fs elapsed\n", + n+m, n, d); + } + /* Append instances in the uncollectable set to a Python * reachable list of garbage. The programmer has to deal with * this if they insist on creating this type of structure. */ - handle_legacy_finalizers(tstate, gcstate, &finalizers, to); - validate_list(to, collecting_clear_unreachable_clear); -} + handle_legacy_finalizers(tstate, gcstate, &finalizers, old); + validate_list(old, collecting_clear_unreachable_clear); -/* Invoke progress callbacks to notify clients that garbage collection - * is starting or stopping - */ -static void -do_gc_callback(GCState *gcstate, const char *phase, - int generation, struct gc_collection_stats *stats) -{ - assert(!PyErr_Occurred()); + /* Clear free list only during the collection of the highest + * generation */ + if (generation == NUM_GENERATIONS-1) { + _PyGC_ClearAllFreeLists(tstate->interp); + } - /* The local variable cannot be rebound, check it for sanity */ - assert(PyList_CheckExact(gcstate->callbacks)); - PyObject *info = NULL; - if (PyList_GET_SIZE(gcstate->callbacks) != 0) { - info = Py_BuildValue("{sisnsn}", - "generation", generation, - "collected", stats->collected, - "uncollectable", stats->uncollectable); - if (info == NULL) { - PyErr_FormatUnraisable("Exception ignored on invoking gc callbacks"); - return; + if (_PyErr_Occurred(tstate)) { + if (reason == _Py_GC_REASON_SHUTDOWN) { + _PyErr_Clear(tstate); + } + else { + PyErr_FormatUnraisable("Exception ignored in garbage collection"); } } - PyObject *phase_obj = PyUnicode_FromString(phase); - if (phase_obj == NULL) { - Py_XDECREF(info); - PyErr_FormatUnraisable("Exception ignored on invoking gc callbacks"); - return; + /* Update stats */ + struct gc_generation_stats *stats = &gcstate->generation_stats[generation]; + stats->collections++; + stats->collected += m; + stats->uncollectable += n; + + GC_STAT_ADD(generation, objects_collected, m); +#ifdef Py_STATS + if (_Py_stats) { + GC_STAT_ADD(generation, object_visits, + _Py_stats->object_stats.object_visits); + _Py_stats->object_stats.object_visits = 0; } +#endif - PyObject *stack[] = {phase_obj, info}; - for (Py_ssize_t i=0; icallbacks); i++) { - PyObject *r, *cb = PyList_GET_ITEM(gcstate->callbacks, i); - Py_INCREF(cb); /* make sure cb doesn't go away */ - r = PyObject_Vectorcall(cb, stack, 2, NULL); - if (r == NULL) { - PyErr_WriteUnraisable(cb); - } - else { - Py_DECREF(r); - } - Py_DECREF(cb); + if (PyDTrace_GC_DONE_ENABLED()) { + PyDTrace_GC_DONE(n + m); } - Py_DECREF(phase_obj); - Py_XDECREF(info); - assert(!PyErr_Occurred()); -} -static void -invoke_gc_callback(GCState *gcstate, const char *phase, - int generation, struct gc_collection_stats *stats) -{ - if (gcstate->callbacks == NULL) { - return; + if (reason != _Py_GC_REASON_SHUTDOWN) { + invoke_gc_callback(tstate, "stop", generation, m, n); } - do_gc_callback(gcstate, phase, generation, stats); + + assert(!_PyErr_Occurred(tstate)); + _Py_atomic_store_int(&gcstate->collecting, 0); + return n + m; } static int @@ -1675,7 +1539,7 @@ _PyGC_GetReferrers(PyInterpreterState *interp, PyObject *objs) } PyObject * -_PyGC_GetObjects(PyInterpreterState *interp, Py_ssize_t generation) +_PyGC_GetObjects(PyInterpreterState *interp, int generation) { assert(generation >= -1 && generation < NUM_GENERATIONS); GCState *gcstate = &interp->gc; @@ -1709,16 +1573,10 @@ void _PyGC_Freeze(PyInterpreterState *interp) { GCState *gcstate = &interp->gc; - gc_list_merge(&gcstate->young.head, &gcstate->permanent_generation.head); - gcstate->young.count = 0; - PyGC_Head*old0 = &gcstate->old[0].head; - PyGC_Head*old1 = &gcstate->old[1].head; - gc_list_merge(old0, &gcstate->permanent_generation.head); - gcstate->old[0].count = 0; - gc_list_set_space(old1, 0); - gc_list_merge(old1, &gcstate->permanent_generation.head); - gcstate->old[1].count = 0; - validate_old(gcstate); + for (int i = 0; i < NUM_GENERATIONS; ++i) { + gc_list_merge(GEN_HEAD(gcstate, i), &gcstate->permanent_generation.head); + gcstate->generations[i].count = 0; + } } void @@ -1726,8 +1584,7 @@ _PyGC_Unfreeze(PyInterpreterState *interp) { GCState *gcstate = &interp->gc; gc_list_merge(&gcstate->permanent_generation.head, - &gcstate->old[0].head); - validate_old(gcstate); + GEN_HEAD(gcstate, NUM_GENERATIONS-1)); } Py_ssize_t @@ -1763,63 +1620,29 @@ PyGC_IsEnabled(void) return gcstate->enabled; } +/* Public API to invoke gc.collect() from C */ Py_ssize_t -_PyGC_Collect(PyThreadState *tstate, int generation, _PyGC_Reason reason) +PyGC_Collect(void) { + PyThreadState *tstate = _PyThreadState_GET(); GCState *gcstate = &tstate->interp->gc; - int expected = 0; - if (!_Py_atomic_compare_exchange_int(&gcstate->collecting, &expected, 1)) { - // Don't start a garbage collection if one is already in progress. + if (!gcstate->enabled) { return 0; } - struct gc_collection_stats stats = { 0 }; - if (reason != _Py_GC_REASON_SHUTDOWN) { - invoke_gc_callback(gcstate, "start", generation, &stats); - } - if (PyDTrace_GC_START_ENABLED()) { - PyDTrace_GC_START(generation); - } + Py_ssize_t n; PyObject *exc = _PyErr_GetRaisedException(tstate); - switch(generation) { - case 0: - gc_collect_young(tstate, &stats); - break; - case 1: - gc_collect_increment(tstate, &stats); - break; - case 2: - gc_collect_full(tstate, &stats); - break; - default: - Py_UNREACHABLE(); - } - if (PyDTrace_GC_DONE_ENABLED()) { - PyDTrace_GC_DONE(stats.uncollectable + stats.collected); - } - if (reason != _Py_GC_REASON_SHUTDOWN) { - invoke_gc_callback(gcstate, "stop", generation, &stats); - } + n = gc_collect_main(tstate, NUM_GENERATIONS - 1, _Py_GC_REASON_MANUAL); _PyErr_SetRaisedException(tstate, exc); - GC_STAT_ADD(generation, objects_collected, stats.collected); -#ifdef Py_STATS - if (_Py_stats) { - GC_STAT_ADD(generation, object_visits, - _Py_stats->object_stats.object_visits); - _Py_stats->object_stats.object_visits = 0; - } -#endif - validate_old(gcstate); - _Py_atomic_store_int(&gcstate->collecting, 0); - return stats.uncollectable + stats.collected; + + return n; } -/* Public API to invoke gc.collect() from C */ Py_ssize_t -PyGC_Collect(void) +_PyGC_Collect(PyThreadState *tstate, int generation, _PyGC_Reason reason) { - return _PyGC_Collect(_PyThreadState_GET(), 2, _Py_GC_REASON_MANUAL); + return gc_collect_main(tstate, generation, reason); } void @@ -1831,7 +1654,7 @@ _PyGC_CollectNoFail(PyThreadState *tstate) during interpreter shutdown (and then never finish it). See http://bugs.python.org/issue8713#msg195178 for an example. */ - _PyGC_Collect(_PyThreadState_GET(), 2, _Py_GC_REASON_SHUTDOWN); + gc_collect_main(tstate, NUM_GENERATIONS - 1, _Py_GC_REASON_SHUTDOWN); } void @@ -1905,9 +1728,9 @@ _PyGC_Fini(PyInterpreterState *interp) * This bug was originally fixed when reported as gh-90228. The bug was * re-introduced in gh-94673. */ - finalize_unlink_gc_head(&gcstate->young.head); - finalize_unlink_gc_head(&gcstate->old[0].head); - finalize_unlink_gc_head(&gcstate->old[1].head); + for (int i = 0; i < NUM_GENERATIONS; i++) { + finalize_unlink_gc_head(&gcstate->generations[i].head); + } finalize_unlink_gc_head(&gcstate->permanent_generation.head); } @@ -1993,10 +1816,10 @@ _PyObject_GC_Link(PyObject *op) GCState *gcstate = &tstate->interp->gc; gc->_gc_next = 0; gc->_gc_prev = 0; - gcstate->young.count++; /* number of allocated GC objects */ - if (gcstate->young.count > gcstate->young.threshold && + gcstate->generations[0].count++; /* number of allocated GC objects */ + if (gcstate->generations[0].count > gcstate->generations[0].threshold && gcstate->enabled && - gcstate->young.threshold && + gcstate->generations[0].threshold && !_Py_atomic_load_int_relaxed(&gcstate->collecting) && !_PyErr_Occurred(tstate)) { @@ -2007,9 +1830,11 @@ _PyObject_GC_Link(PyObject *op) void _Py_RunGC(PyThreadState *tstate) { - if (tstate->interp->gc.enabled) { - _PyGC_Collect(tstate, 1, _Py_GC_REASON_HEAP); + GCState *gcstate = get_gc_state(); + if (!gcstate->enabled) { + return; } + gc_collect_main(tstate, GENERATION_AUTO, _Py_GC_REASON_HEAP); } static PyObject * @@ -2117,8 +1942,8 @@ PyObject_GC_Del(void *op) #endif } GCState *gcstate = get_gc_state(); - if (gcstate->young.count > 0) { - gcstate->young.count--; + if (gcstate->generations[0].count > 0) { + gcstate->generations[0].count--; } PyObject_Free(((char *)op)-presize); } @@ -2141,36 +1966,26 @@ PyObject_GC_IsFinalized(PyObject *obj) return 0; } -static int -visit_generation(gcvisitobjects_t callback, void *arg, struct gc_generation *gen) -{ - PyGC_Head *gc_list, *gc; - gc_list = &gen->head; - for (gc = GC_NEXT(gc_list); gc != gc_list; gc = GC_NEXT(gc)) { - PyObject *op = FROM_GC(gc); - Py_INCREF(op); - int res = callback(op, arg); - Py_DECREF(op); - if (!res) { - return -1; - } - } - return 0; -} - void PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg) { + size_t i; GCState *gcstate = get_gc_state(); int origenstate = gcstate->enabled; gcstate->enabled = 0; - if (visit_generation(callback, arg, &gcstate->young)) { - goto done; - } - if (visit_generation(callback, arg, &gcstate->old[0])) { - goto done; + for (i = 0; i < NUM_GENERATIONS; i++) { + PyGC_Head *gc_list, *gc; + gc_list = GEN_HEAD(gcstate, i); + for (gc = GC_NEXT(gc_list); gc != gc_list; gc = GC_NEXT(gc)) { + PyObject *op = FROM_GC(gc); + Py_INCREF(op); + int res = callback(op, arg); + Py_DECREF(op); + if (!res) { + goto done; + } + } } - visit_generation(callback, arg, &gcstate->old[1]); done: gcstate->enabled = origenstate; } diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 2d653617d37080..02bbdfd4999d49 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -744,7 +744,7 @@ void _PyGC_InitState(GCState *gcstate) { // TODO: move to pycore_runtime_init.h once the incremental GC lands. - gcstate->young.threshold = 2000; + gcstate->generations[0].threshold = 2000; } @@ -1042,8 +1042,8 @@ cleanup_worklist(struct worklist *worklist) static bool gc_should_collect(GCState *gcstate) { - int count = _Py_atomic_load_int_relaxed(&gcstate->young.count); - int threshold = gcstate->young.threshold; + int count = _Py_atomic_load_int_relaxed(&gcstate->generations[0].count); + int threshold = gcstate->generations[0].threshold; if (count <= threshold || threshold == 0 || !gcstate->enabled) { return false; } @@ -1051,7 +1051,7 @@ gc_should_collect(GCState *gcstate) // objects. A few tests rely on immediate scheduling of the GC so we ignore // the scaled threshold if generations[1].threshold is set to zero. return (count > gcstate->long_lived_total / 4 || - gcstate->old[0].threshold == 0); + gcstate->generations[1].threshold == 0); } static void @@ -1065,7 +1065,7 @@ record_allocation(PyThreadState *tstate) if (gc->alloc_count >= LOCAL_ALLOC_COUNT_THRESHOLD) { // TODO: Use Py_ssize_t for the generation count. GCState *gcstate = &tstate->interp->gc; - _Py_atomic_add_int(&gcstate->young.count, (int)gc->alloc_count); + _Py_atomic_add_int(&gcstate->generations[0].count, (int)gc->alloc_count); gc->alloc_count = 0; if (gc_should_collect(gcstate) && @@ -1084,7 +1084,7 @@ record_deallocation(PyThreadState *tstate) gc->alloc_count--; if (gc->alloc_count <= -LOCAL_ALLOC_COUNT_THRESHOLD) { GCState *gcstate = &tstate->interp->gc; - _Py_atomic_add_int(&gcstate->young.count, (int)gc->alloc_count); + _Py_atomic_add_int(&gcstate->generations[0].count, (int)gc->alloc_count); gc->alloc_count = 0; } } @@ -1207,11 +1207,10 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) /* update collection and allocation counters */ if (generation+1 < NUM_GENERATIONS) { - gcstate->old[generation].count += 1; + gcstate->generations[generation+1].count += 1; } - gcstate->young.count = 0; - for (i = 1; i <= generation; i++) { - gcstate->old[i-1].count = 0; + for (i = 0; i <= generation; i++) { + gcstate->generations[i].count = 0; } PyInterpreterState *interp = tstate->interp; @@ -1377,7 +1376,7 @@ visit_get_objects(const mi_heap_t *heap, const mi_heap_area_t *area, } PyObject * -_PyGC_GetObjects(PyInterpreterState *interp, Py_ssize_t generation) +_PyGC_GetObjects(PyInterpreterState *interp, int generation) { PyObject *result = PyList_New(0); if (!result) { diff --git a/Python/optimizer.c b/Python/optimizer.c index 50ab71c5c01324..382c188515041f 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -1214,7 +1214,7 @@ make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFil static int init_cold_exit_executor(_PyExecutorObject *executor, int oparg) { - _Py_SetImmortalUntracked((PyObject *)executor); + _Py_SetImmortal((PyObject *)executor); Py_SET_TYPE(executor, &_PyUOpExecutor_Type); executor->trace = (_PyUOpInstruction *)executor->exits; executor->code_size = 1; diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 5fdc812a00f059..97e19927b22866 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -1761,11 +1761,8 @@ def is_waiting_for_gil(self): return (name == 'take_gil') def is_gc_collect(self): - '''Is this frame a collector within the garbage-collector?''' - return self._gdbframe.name() in ( - 'collect', 'gc_collect_full', 'gc_collect_main', - 'gc_collect_young', 'gc_collect_increment', - ) + '''Is this frame gc_collect_main() within the garbage-collector?''' + return self._gdbframe.name() in ('collect', 'gc_collect_main') def get_pyop(self): try: From ffcd5134378d3c89f4e27e5c84433b2aafef9d25 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sun, 29 Sep 2024 23:21:00 +0200 Subject: [PATCH 08/15] Fix merge snafu in What's New. --- Doc/whatsnew/3.13.rst | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 5a7ba886749a9b..05d99064db2486 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -908,7 +908,6 @@ fractions (Contributed by Mark Dickinson in :gh:`111320`.) -<<<<<<< HEAD gc -- @@ -939,31 +938,6 @@ may not work exactly as intended, but it is very unlikely to be harmful. All other code will work just fine. -||||||| 15309329b65 (GH-108362: Incremental Cycle GC (GH-116206)) -gc --- - -* The cyclic garbage collector is now incremental, which changes the meanings - of the results of :meth:`gc.get_threshold` and :meth:`gc.get_threshold` as - well as :meth:`gc.get_count` and :meth:`gc.get_stats`. -* :meth:`gc.get_threshold` returns a three-tuple for backwards compatibility, - the first value is the threshold for young collections, as before, the second - value determines the rate at which the old collection is scanned; the - default is 10 and higher values mean that the old collection is scanned more slowly. - The third value is meangless and is always zero. -* :meth:`gc.set_threshold` ignores any items after the second. -* :meth:`gc.get_count` and :meth:`gc.get_stats`. - These functions return the same format of results as before. - The only difference is that instead of the results refering to - the young, aging and old generations, the results refer to the - young generation and the aging and collecting spaces of the old generation. - -In summary, code that attempted to manipulate the behavior of the cycle GC may -not work exactly as intended, but it is very unlikely to harmful. -All other code will work just fine. - -======= ->>>>>>> parent of 15309329b65 (GH-108362: Incremental Cycle GC (GH-116206)) glob ---- From 03caeb5847150282cb41a2844368212fe4c33a67 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sun, 29 Sep 2024 23:33:13 +0200 Subject: [PATCH 09/15] Fix the free-threaded build. --- Include/internal/pycore_gc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index b2df172f65dd64..357177bcd6fd84 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -302,7 +302,6 @@ struct _gc_runtime_state { /* a list of callbacks to be invoked when collection is performed */ PyObject *callbacks; -#ifndef Py_GIL_DISABLED /* This is the number of objects that survived the last full collection. It approximates the number of long lived objects tracked by the GC. @@ -315,6 +314,7 @@ struct _gc_runtime_state { the first time. */ Py_ssize_t long_lived_pending; +#ifdef Py_GIL_DISABLED /* gh-117783: Deferred reference counting is not fully implemented yet, so as a temporary measure we treat objects using deferred reference counting as immortal. The value may be zero, one, or a negative number: From cff04368184d06e463aaf0b942ac896f0acf987b Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sun, 29 Sep 2024 23:40:13 +0200 Subject: [PATCH 10/15] Undo a few revers where there is no harm in the original versions. --- Python/optimizer.c | 2 +- Tools/gdb/libpython.py | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index 382c188515041f..50ab71c5c01324 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -1214,7 +1214,7 @@ make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFil static int init_cold_exit_executor(_PyExecutorObject *executor, int oparg) { - _Py_SetImmortal((PyObject *)executor); + _Py_SetImmortalUntracked((PyObject *)executor); Py_SET_TYPE(executor, &_PyUOpExecutor_Type); executor->trace = (_PyUOpInstruction *)executor->exits; executor->code_size = 1; diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 97e19927b22866..5fdc812a00f059 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -1761,8 +1761,11 @@ def is_waiting_for_gil(self): return (name == 'take_gil') def is_gc_collect(self): - '''Is this frame gc_collect_main() within the garbage-collector?''' - return self._gdbframe.name() in ('collect', 'gc_collect_main') + '''Is this frame a collector within the garbage-collector?''' + return self._gdbframe.name() in ( + 'collect', 'gc_collect_full', 'gc_collect_main', + 'gc_collect_young', 'gc_collect_increment', + ) def get_pyop(self): try: From a0d5270203fbddd9359f56a073dcb5e0d0f184d9 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Mon, 30 Sep 2024 01:02:48 +0200 Subject: [PATCH 11/15] Fix TSan build of freethreaded python. --- Python/gc_free_threading.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 02bbdfd4999d49..140f1eaa88bfc2 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -1090,9 +1090,18 @@ record_deallocation(PyThreadState *tstate) } static void -gc_collect_internal(PyInterpreterState *interp, struct collection_state *state) +gc_collect_internal(PyInterpreterState *interp, struct collection_state *state, int generation) { _PyEval_StopTheWorld(interp); + + // update collection and allocation counters + if (generation+1 < NUM_GENERATIONS) { + state->gcstate->generations[generation+1].count += 1; + } + for (int i = 0; i <= generation; i++) { + state->gcstate->generations[i].count = 0; + } + // merge refcounts for all queued objects merge_all_queued_objects(interp, state); process_delayed_frees(interp); @@ -1158,7 +1167,6 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state) static Py_ssize_t gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) { - int i; Py_ssize_t m = 0; /* # objects collected */ Py_ssize_t n = 0; /* # unreachable objects that couldn't be collected */ PyTime_t t1 = 0; /* initialize to prevent a compiler warning */ @@ -1205,14 +1213,6 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) PyDTrace_GC_START(generation); } - /* update collection and allocation counters */ - if (generation+1 < NUM_GENERATIONS) { - gcstate->generations[generation+1].count += 1; - } - for (i = 0; i <= generation; i++) { - gcstate->generations[i].count = 0; - } - PyInterpreterState *interp = tstate->interp; struct collection_state state = { @@ -1220,7 +1220,7 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) .gcstate = gcstate, }; - gc_collect_internal(interp, &state); + gc_collect_internal(interp, &state, generation); m = state.collected; n = state.uncollectable; From 1ba3555c287d7529f3f40e8999c777fd17f061cc Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Mon, 30 Sep 2024 02:49:23 +0200 Subject: [PATCH 12/15] Reinstate cast. --- Modules/gcmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 3235ad67c70d40..b57a1c90723306 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -331,7 +331,7 @@ gc_get_objects_impl(PyObject *module, Py_ssize_t generation) } PyInterpreterState *interp = _PyInterpreterState_GET(); - return _PyGC_GetObjects(interp, generation); + return _PyGC_GetObjects(interp, (int)generation); } /*[clinic input] From f95093d49ad7514f9220f58dd10150895b006bce Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Mon, 30 Sep 2024 22:39:11 +0200 Subject: [PATCH 13/15] Update the ABI. --- Doc/data/python3.13.abi | 9810 +++++++++++++++++++-------------------- 1 file changed, 4904 insertions(+), 4906 deletions(-) diff --git a/Doc/data/python3.13.abi b/Doc/data/python3.13.abi index 149ff4b91d5e2c..55112e1e43c01d 100644 --- a/Doc/data/python3.13.abi +++ b/Doc/data/python3.13.abi @@ -1679,7 +1679,7 @@ - + @@ -1694,7 +1694,7 @@ - + @@ -1728,10 +1728,10 @@ - + - + @@ -1819,11 +1819,11 @@ - + - + @@ -1835,14 +1835,14 @@ - + - + @@ -1861,7 +1861,7 @@ - + @@ -1889,7 +1889,7 @@ - + @@ -1931,7 +1931,7 @@ - + @@ -1948,7 +1948,7 @@ - + @@ -1961,7 +1961,7 @@ - + @@ -1994,13 +1994,13 @@ - + - + @@ -2008,7 +2008,7 @@ - + @@ -2093,7 +2093,7 @@ - + @@ -2118,7 +2118,7 @@ - + @@ -2157,7 +2157,7 @@ - + @@ -3384,7 +3384,7 @@ - + @@ -3438,7 +3438,7 @@ - + @@ -3477,20 +3477,20 @@ - - + + - + - + - + - + @@ -3552,7 +3552,7 @@ - + @@ -3561,7 +3561,7 @@ - + @@ -3647,7 +3647,7 @@ - + @@ -3669,12 +3669,12 @@ - + - + @@ -3691,7 +3691,7 @@ - + @@ -3881,7 +3881,7 @@ - + @@ -3958,44 +3958,44 @@ - - + + - - + + - + - + - + - + - + - + @@ -4055,7 +4055,7 @@ - + @@ -4167,12 +4167,12 @@ - + - + @@ -4226,24 +4226,24 @@ - + - + - + - + @@ -4802,7 +4802,7 @@ - + @@ -4834,13 +4834,13 @@ - + - + @@ -5034,7 +5034,7 @@ - + @@ -5070,22 +5070,22 @@ - + - + - + - + @@ -5191,7 +5191,7 @@ - + @@ -5249,7 +5249,7 @@ - + @@ -5312,7 +5312,7 @@ - + @@ -5446,7 +5446,7 @@ - + @@ -5506,7 +5506,7 @@ - + @@ -5538,12 +5538,12 @@ - + - + @@ -5650,7 +5650,7 @@ - + @@ -5725,9 +5725,9 @@ - + - + @@ -5747,7 +5747,7 @@ - + @@ -5763,21 +5763,21 @@ - + - + - + - + @@ -5803,7 +5803,7 @@ - + @@ -5840,7 +5840,7 @@ - + @@ -6003,7 +6003,7 @@ - + @@ -6120,7 +6120,7 @@ - + @@ -6181,18 +6181,18 @@ - + - + - + @@ -6203,11 +6203,11 @@ - + - + @@ -6262,7 +6262,7 @@ - + @@ -6286,12 +6286,12 @@ - + - + @@ -6315,181 +6315,181 @@ - - + + - - - - + + + + - - - - - + + + + + - - - + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - + + + - - - - + + + + - - - - - + + + + + - - + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - + + + + - - + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - - + + + + - - - + + + - - + + - - - + + + - - - + + + - - + + - - + + @@ -6498,7 +6498,7 @@ - + @@ -6507,7 +6507,7 @@ - + @@ -6579,11 +6579,11 @@ - + - + @@ -6848,7 +6848,7 @@ - + @@ -6937,7 +6937,7 @@ - + @@ -7141,7 +7141,7 @@ - + @@ -7226,11 +7226,11 @@ - + - + @@ -7302,7 +7302,7 @@ - + @@ -7404,7 +7404,7 @@ - + @@ -7424,12 +7424,12 @@ - + - + @@ -7438,7 +7438,7 @@ - + @@ -7631,7 +7631,7 @@ - + @@ -7644,7 +7644,7 @@ - + @@ -7673,7 +7673,7 @@ - + @@ -7728,7 +7728,7 @@ - + @@ -7753,8 +7753,8 @@ - - + + @@ -7924,7 +7924,7 @@ - + @@ -7952,7 +7952,7 @@ - + @@ -7979,7 +7979,7 @@ - + @@ -8078,7 +8078,7 @@ - + @@ -8171,7 +8171,7 @@ - + @@ -8188,7 +8188,7 @@ - + @@ -8313,21 +8313,21 @@ - + - + - - - + + + @@ -8368,8 +8368,8 @@ - - + + @@ -8502,59 +8502,59 @@ - - + + - - + + - - + + - - + + - - - + + + - - + + - - + + - - + + - - - + + + - - - + + + - - + + - - + + - - + + @@ -8601,7 +8601,7 @@ - + @@ -9175,10 +9175,9 @@ - - - + + @@ -9188,17 +9187,9 @@ - + - - - - - - - - @@ -9214,9 +9205,9 @@ - + - + @@ -9225,7 +9216,7 @@ - + @@ -9239,17 +9230,17 @@ - + - - + + - + @@ -9374,11 +9365,11 @@ - + - + @@ -9398,12 +9389,12 @@ - + - + @@ -9438,23 +9429,23 @@ - + - - - - + + + + - - - + + + - - - + + + @@ -9481,7 +9472,7 @@ - + @@ -9496,7 +9487,7 @@ - + @@ -9507,7 +9498,7 @@ - + @@ -9515,11 +9506,11 @@ - - - + + + - + @@ -9527,8 +9518,8 @@ - - + + @@ -9542,7 +9533,7 @@ - + @@ -9551,15 +9542,15 @@ - + - - - + + + @@ -9575,30 +9566,30 @@ - - - + + + - - + + - - + + - - - + + + - - + + - + @@ -9626,72 +9617,72 @@ - - + + - + - + - - + + - + - + - + - + - + - - - + + + - - + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - + + - + @@ -9711,7 +9702,7 @@ - + @@ -9789,28 +9780,28 @@ - + - + - + - + - + @@ -9834,9 +9825,9 @@ - - - + + + @@ -9859,7 +9850,7 @@ - + @@ -9899,18 +9890,18 @@ - + - + - + @@ -9976,7 +9967,7 @@ - + @@ -9994,7 +9985,7 @@ - + @@ -10016,16 +10007,16 @@ - - - + + + - + - + - + @@ -10037,11 +10028,11 @@ - - - - - + + + + + @@ -10075,11 +10066,11 @@ - + - + @@ -10102,7 +10093,7 @@ - + @@ -10129,7 +10120,7 @@ - + @@ -10138,33 +10129,33 @@ - + + - - - - + + + - + - + - + - + @@ -10214,22 +10205,22 @@ - + - + - + - + @@ -10267,410 +10258,410 @@ - - - + + + - - - - - - + + + + + + - - - + + + - - - + + + - - - - + + + + - - - - - - + + + + + + - - - + + + - - - + + + - - + + - - + + - - - - + + + + - - - - + + + + - - - - + + + + - - - + + + - - + + - - - - + + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - + + - - + + - - + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - + + + + + + - - - - + + + + - - + + - - - - - + + + + + - - - - - - + + + + + + - - - - + + + + - - + + - - - - + + + + - - + + - - - - + + + + - - + + - - + + - - - - - + + + + + - - + + - - - + + + - - - - + + + + - - - - - + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - + + + + - - - - - + + + + + - - - + + + - - - + + + - - - - + + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - - + + + + + - - - - + + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - - - + + + + + - - - - + + + + - - - + + + - - + + - - - + + + - - + + - - + + - + - + - + @@ -10684,8 +10675,8 @@ - - + + @@ -10699,22 +10690,22 @@ - + - + - - + + - + @@ -10740,35 +10731,35 @@ - - + + - + - + - + - + - + - + - + - + - + @@ -10800,8 +10791,8 @@ - - + + @@ -10809,11 +10800,11 @@ - + - - + + @@ -10824,17 +10815,17 @@ - - - - - - - - - - - + + + + + + + + + + + @@ -10842,11 +10833,11 @@ - + - - + + @@ -10854,11 +10845,11 @@ - + - - + + @@ -10866,11 +10857,11 @@ - + - - + + @@ -10878,11 +10869,11 @@ - + - - + + @@ -10890,11 +10881,11 @@ - + - - + + @@ -10902,11 +10893,11 @@ - + - - + + @@ -10914,11 +10905,11 @@ - + - - + + @@ -10926,66 +10917,66 @@ - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -11016,12 +11007,12 @@ - + - + - + @@ -11036,286 +11027,286 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -11330,28 +11321,28 @@ - + - + - + - + - + - + - + - + - + @@ -11366,26 +11357,26 @@ - + - + - + - + - + - + - + - + @@ -11396,12 +11387,12 @@ - + - + - + @@ -11416,117 +11407,117 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -11541,57 +11532,57 @@ - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - - + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + @@ -11629,7 +11620,7 @@ - + @@ -11659,22 +11650,22 @@ - + - + - + - + - + @@ -11713,16 +11704,16 @@ - + - + - + @@ -11765,214 +11756,214 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + - - - - - + + + + + - - + + + - - + - - + + - + - + - - + + - + - - + + - - - + + + - - + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - + + + + + + + + + - - - + + + - - + + - - + + - - + + - - - + + + @@ -11985,36 +11976,36 @@ - + + - - + - + - - + + - + - - + + - + - + @@ -12026,10 +12017,10 @@ - + - + @@ -12065,10 +12056,10 @@ - + - + @@ -12080,9 +12071,9 @@ - + - + @@ -12119,13 +12110,13 @@ - + - + @@ -12133,24 +12124,24 @@ - + - + - + - - + + @@ -12159,16 +12150,17 @@ - - + + - + + @@ -12179,6 +12171,14 @@ + + + + + + + + @@ -12197,18 +12197,6 @@ - - - - - - - - - - - - @@ -12242,8 +12230,8 @@ - - + + @@ -12251,8 +12239,8 @@ - - + + @@ -12265,71 +12253,71 @@ - - + + - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + - + - + - + - - + + - + - - + + @@ -12337,8 +12325,8 @@ - - + + @@ -12346,1054 +12334,1054 @@ - - + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - + + + - - - + + + - - - + + + - + - - + + - + - - + + - - + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - - - + + + + + - - + + - - - - - + + + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - - - + + + + - - + + - - - - + + + + - - + + - - + + - - + + - + - - + + - - + + - - + + - + - - + + - + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - - + + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - - + + + - - + + - - + + - - + + - + - - + + - + - - + + - + - - + + + + - - - - + + - - - + + + - - + + + + - - - - + + - - - + + + - - + + - - + + - - + + - - - - + + + + - - - - - + + + + + - + - - + + - + - - + + - + - - + + - - - + + + - - + + - - - - + + + + - - + + - + - - + + - - + + - - + + - + - - + + - - - + + + - - + + - - + + - - + + - - + + - - + + - + - + - + - - + + - - + + - - + + - - + + - + - + - + - + - + - + - - + + - - + + - - + + - - + + - + - + - - - + + + - + - + - + - + - + - - - + + + - + - + - + - - + + - - + + - + - - - + + + - - - - + + + + - + - + - - + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - - + + + + - - - - + + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - - + + + + - - - - + + + + - - - - + + + + - - + + + + + - - - - + - - + + + + + + + - - - - - - - + + - - - + + + - - - - + + + + - - - - + + + + - + - + - - - + + + - - - + + + - - - + + + - - + + + - - + + - - + + - - + - - + + - - + + - - - + + + - - - + + + - - - - + + + + + - - - + + + - - - + + + - - + - - + + - - - + + + - - + + - - + + - - - + + + - - + + + - - - - - - + + + + + - + - - - + + + - - + + - + - - + + - - + + @@ -13401,8 +13389,8 @@ - - + + @@ -13419,147 +13407,147 @@ - - + + - - + + - - - + + + - + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - - - + + + - - + + - - + + - + - + - + - + - - + + - - + + - - + + - + - + - - + + - - + + - - + + - - + + - - + + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - + - - + + - - - - - - + + + + + + @@ -13573,8 +13561,8 @@ - - + + @@ -13600,7 +13588,7 @@ - + @@ -13617,178 +13605,178 @@ - - + + - + - - + + - + - - + + - - + + - + - - + + - + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - - + + + - + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - - + + - - + + - + - + - + @@ -13796,23 +13784,23 @@ - + - - + + - + - - + + - + - - + + @@ -13826,8 +13814,8 @@ - - + + @@ -13835,8 +13823,8 @@ - - + + @@ -13844,35 +13832,35 @@ - + - - + + - + - + - + - + - + - + - + - - + + @@ -13943,16 +13931,16 @@ - + - + - + @@ -13961,17 +13949,17 @@ - + - + - - - + + + @@ -13979,9 +13967,9 @@ - - - + + + @@ -13993,7 +13981,7 @@ - + @@ -14005,9 +13993,9 @@ - - - + + + @@ -14021,11 +14009,11 @@ - + - - + + @@ -14034,10 +14022,10 @@ - - - - + + + + @@ -14045,8 +14033,8 @@ - - + + @@ -14099,8 +14087,8 @@ - - + + @@ -14108,9 +14096,9 @@ - - - + + + @@ -14118,7 +14106,7 @@ - + @@ -14126,8 +14114,8 @@ - - + + @@ -14159,8 +14147,8 @@ - - + + @@ -14222,16 +14210,16 @@ - + - + - + - + @@ -14312,7 +14300,7 @@ - + @@ -14360,8 +14348,8 @@ - - + + @@ -14372,43 +14360,43 @@ - - + + - + - + - + - - + + - + - + - - + + - + @@ -14417,7 +14405,7 @@ - + @@ -14438,188 +14426,188 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - + - + - + - - + + - + - + - + - - - + + + - + - + - + - + - - + + - + - + - - + + @@ -14633,49 +14621,49 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -14690,16 +14678,16 @@ - + - + - + @@ -14717,22 +14705,22 @@ - + - + - + - + - + @@ -14756,13 +14744,13 @@ - + - + @@ -14774,27 +14762,27 @@ - - + + - - - + + + - + - + - - + + @@ -14820,39 +14808,39 @@ - - - + + + - + - + - + - + - - - + + + - + - - + + - + @@ -14864,8 +14852,8 @@ - - + + @@ -14879,7 +14867,7 @@ - + @@ -14909,10 +14897,10 @@ - + - + @@ -14966,7 +14954,7 @@ - + @@ -14990,7 +14978,7 @@ - + @@ -15019,18 +15007,18 @@ - - + + - + - - + + @@ -15038,8 +15026,8 @@ - - + + @@ -15050,10 +15038,10 @@ - + - + @@ -15070,10 +15058,10 @@ - - + + - + @@ -15082,18 +15070,18 @@ - - - - + + + + - + - + @@ -15102,7 +15090,7 @@ - + @@ -15119,10 +15107,10 @@ - - - - + + + + @@ -15130,25 +15118,25 @@ - + - - - + + + - - + + - - + + @@ -15164,20 +15152,20 @@ - - + + - - - - - - + + + + + + @@ -15185,11 +15173,11 @@ - + - - + + @@ -15197,11 +15185,11 @@ - + - - + + @@ -15209,11 +15197,11 @@ - + - - + + @@ -15221,11 +15209,11 @@ - + - - + + @@ -15255,12 +15243,12 @@ - + - + - + @@ -15275,329 +15263,329 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -15612,12 +15600,12 @@ - + - + - + @@ -15632,9 +15620,9 @@ - + - + @@ -16369,19 +16357,19 @@ - - + + - + - + - + @@ -16389,11 +16377,11 @@ - + - - + + @@ -16404,16 +16392,16 @@ - - + + - + - + - + @@ -16422,20 +16410,20 @@ - + - + - + - + - + @@ -16443,11 +16431,11 @@ - - - + + + - + @@ -16456,24 +16444,24 @@ - + - + - + - + - + - + @@ -16482,19 +16470,19 @@ - + - - + + - + - + @@ -16506,20 +16494,20 @@ - + - + - + - + - + - + @@ -16528,10 +16516,10 @@ - + - + @@ -16540,7 +16528,7 @@ - + @@ -16548,27 +16536,27 @@ - + - + - + - + - + - + @@ -16576,16 +16564,16 @@ - - + + - + - + - + @@ -16599,7 +16587,7 @@ - + @@ -16613,21 +16601,21 @@ - + - - + + - + - + @@ -16636,10 +16624,10 @@ - - - - + + + + @@ -16650,19 +16638,19 @@ - + - + - - + + - + - + @@ -16674,10 +16662,10 @@ - + - + @@ -16685,26 +16673,26 @@ - + - + - + - + - + - + - + @@ -16712,13 +16700,13 @@ - + - - + + - + @@ -16733,29 +16721,29 @@ - + - + - + - + - + - + - + @@ -16764,8 +16752,8 @@ - - + + @@ -16782,21 +16770,21 @@ - + - + - + - + - + @@ -16805,7 +16793,7 @@ - + @@ -16822,7 +16810,7 @@ - + @@ -16848,10 +16836,10 @@ - + - + @@ -16859,7 +16847,7 @@ - + @@ -16871,7 +16859,7 @@ - + @@ -16958,7 +16946,7 @@ - + @@ -16966,10 +16954,10 @@ - + - + @@ -16982,7 +16970,7 @@ - + @@ -16998,12 +16986,12 @@ - + - + @@ -17011,7 +16999,7 @@ - + @@ -17019,7 +17007,7 @@ - + @@ -17065,7 +17053,7 @@ - + @@ -17078,7 +17066,7 @@ - + @@ -17090,73 +17078,70 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - + - + - - + + - - + + - + - + - + - + - + - + @@ -17170,66 +17155,66 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -17243,7 +17228,7 @@ - + @@ -17264,12 +17249,12 @@ - + - + - + @@ -17277,2499 +17262,2499 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + @@ -19778,8 +19763,8 @@ - - + + @@ -19790,25 +19775,25 @@ - - + + - + - - + + - + - - + + - + - + @@ -19817,22 +19802,22 @@ - - - - - - - + + + + + + + - + - + - - + + @@ -19840,50 +19825,50 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -19906,13 +19891,13 @@ - + - + - + @@ -19923,14 +19908,14 @@ - + - + - + @@ -19942,7 +19927,7 @@ - + @@ -19951,7 +19936,7 @@ - + @@ -19968,10 +19953,10 @@ - - + + - + @@ -19986,7 +19971,7 @@ - + @@ -20004,10 +19989,10 @@ - + - + @@ -20025,13 +20010,13 @@ - + - + - + @@ -20049,10 +20034,10 @@ - + - + @@ -20061,10 +20046,10 @@ - + - + @@ -20076,82 +20061,82 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -20166,25 +20151,25 @@ - + - + - + - + - + - + @@ -20201,23 +20186,23 @@ - + - + - + - + - - + + - + @@ -20226,14 +20211,14 @@ - - + + - - + + @@ -20241,15 +20226,15 @@ - + - + - + @@ -20257,86 +20242,86 @@ - - - + + + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -20351,34 +20336,34 @@ - + - + - + - - + + - + - + - + - + - + - - + + - + @@ -20387,7 +20372,7 @@ - + @@ -20395,33 +20380,33 @@ - + - + - + - + - + - + - + - - + + - + - - + + @@ -20438,17 +20423,17 @@ - + - + - - + + @@ -20459,29 +20444,29 @@ - + - + - + - + - + - + @@ -20489,28 +20474,28 @@ - - + + - + - + - - + + - + - + @@ -20528,46 +20513,46 @@ - + - - - - + + + + - + - + - + - - + + - + - + - + - + - + @@ -20575,25 +20560,25 @@ - - + + - + - + - + - + - + @@ -20604,53 +20589,53 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -20662,18 +20647,18 @@ - + - + - + - + - + @@ -20681,24 +20666,24 @@ - + - + - + - + - + @@ -20706,9 +20691,9 @@ - + - + @@ -20717,10 +20702,10 @@ - - + + - + @@ -20729,52 +20714,52 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -20785,7 +20770,7 @@ - + @@ -20826,7 +20811,7 @@ - + @@ -20855,7 +20840,7 @@ - + @@ -20875,7 +20860,7 @@ - + @@ -20907,7 +20892,7 @@ - + @@ -20915,7 +20900,7 @@ - + @@ -20929,7 +20914,7 @@ - + @@ -20940,7 +20925,7 @@ - + @@ -20951,7 +20936,7 @@ - + @@ -20962,7 +20947,7 @@ - + @@ -20970,7 +20955,7 @@ - + @@ -20981,7 +20966,7 @@ - + @@ -20992,7 +20977,7 @@ - + @@ -21006,7 +20991,7 @@ - + @@ -21014,8 +20999,8 @@ - - + + @@ -21025,7 +21010,7 @@ - + @@ -21049,7 +21034,7 @@ - + @@ -21058,70 +21043,70 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -21130,33 +21115,33 @@ - + - + - + - + - + - + - + - + - + @@ -21168,20 +21153,20 @@ - + - + - + - + - + - + @@ -21196,7 +21181,7 @@ - + @@ -21204,17 +21189,17 @@ - + - + - + - + @@ -21223,13 +21208,13 @@ - + - + @@ -21237,9 +21222,9 @@ - + - + @@ -21248,18 +21233,18 @@ - + - + - + - + - + @@ -21268,28 +21253,28 @@ - + - + - + - + - + - + - + - + @@ -21300,35 +21285,35 @@ - + - + - + - + - - + + - + - + - + - + @@ -21336,7 +21321,7 @@ - + @@ -21347,12 +21332,12 @@ - + - + - + @@ -21375,33 +21360,33 @@ - - + + - + - + - + - + - + - + - + @@ -21409,33 +21394,33 @@ - + - - + + - + - + - - + + - + - + - + - + @@ -21446,10 +21431,10 @@ - + - + @@ -21457,18 +21442,18 @@ - + - + - + - + - + @@ -21479,14 +21464,14 @@ - + - - + + @@ -21500,23 +21485,23 @@ - + - + - + - + - + @@ -21524,43 +21509,43 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + @@ -21595,25 +21580,25 @@ - - - + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + @@ -21627,19 +21612,19 @@ - + - + - + - + - + @@ -21647,44 +21632,44 @@ - - + + - + - - - + + + - + - + - - + + - + - + - - + + - + @@ -21696,22 +21681,22 @@ - + - + - + - - - - - - - - + + + + + + + + @@ -21734,30 +21719,30 @@ - + - + - + - + - - + + - + - + - + - + @@ -21766,33 +21751,33 @@ - + - - - - - - - + + + + + + + - + - - - + + + - + - - + + @@ -21800,9 +21785,9 @@ - - - + + + @@ -21816,36 +21801,36 @@ - + - + - + - + - + - + - + - + - + - + - + @@ -21853,7 +21838,7 @@ - + @@ -21861,10 +21846,10 @@ - + - + @@ -21872,10 +21857,10 @@ - + - + @@ -21892,7 +21877,7 @@ - + @@ -21900,18 +21885,18 @@ - + - + - + - + - + @@ -21919,7 +21904,7 @@ - + @@ -21927,7 +21912,7 @@ - + @@ -21938,8 +21923,8 @@ - - + + @@ -21950,9 +21935,9 @@ - - - + + + @@ -21990,10 +21975,10 @@ - + - + @@ -22002,31 +21987,31 @@ - + - + - + - + - + - + - + @@ -22038,25 +22023,25 @@ - + - - + + - + - + - + @@ -22082,14 +22067,14 @@ - + - + - + @@ -22137,8 +22122,8 @@ - - + + @@ -22176,7 +22161,7 @@ - + @@ -22206,22 +22191,22 @@ - + - + - + - + - + @@ -22260,16 +22245,16 @@ - + - + - + @@ -22284,7 +22269,7 @@ - + @@ -22295,11 +22280,11 @@ - + - - + + @@ -22322,14 +22307,14 @@ - + - - + + @@ -22337,10 +22322,10 @@ - - + + - + @@ -22349,7 +22334,7 @@ - + @@ -22357,13 +22342,13 @@ - - + + - + - + @@ -22375,10 +22360,10 @@ - + - + @@ -22414,10 +22399,10 @@ - + - + @@ -22429,194 +22414,195 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + - - - + + + + - - - - + + + + - + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + - + + + - - - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - + + + + + + - - - - + + + + + + - + - - - - - - - + + + + + + + @@ -22656,24 +22642,24 @@ - - + + - - + + - - + + - - + + @@ -22681,12 +22667,12 @@ - + - + @@ -22717,7 +22703,7 @@ - + @@ -22816,24 +22802,24 @@ - - + + - + - + - + - + @@ -22841,11 +22827,11 @@ - + - + @@ -22853,7 +22839,7 @@ - + @@ -22864,303 +22850,303 @@ + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - + - + - - + + - - + + - - + + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -23172,7 +23158,7 @@ - + @@ -23213,7 +23199,7 @@ - + @@ -23222,7 +23208,7 @@ - + @@ -23267,15 +23253,15 @@ - - - + + + - + - - + + @@ -23292,10 +23278,10 @@ - - + + - + @@ -23339,7 +23325,7 @@ - + @@ -23397,41 +23383,41 @@ - + - + - + - + - + - + - + - + @@ -23440,43 +23426,43 @@ - - - - + + + + - - + + - + - - + + - + - + - + - + @@ -23485,13 +23471,13 @@ - - - - + + + + - + @@ -23505,7 +23491,7 @@ - + @@ -23533,13 +23519,13 @@ - + - + @@ -23607,8 +23593,8 @@ - - + + @@ -23658,7 +23644,7 @@ - + @@ -23669,8 +23655,8 @@ - - + + @@ -23678,10 +23664,10 @@ - + - + @@ -23690,13 +23676,13 @@ - - + + - + @@ -23717,8 +23703,8 @@ - - + + @@ -23732,16 +23718,16 @@ - - - - + + + + - + - + @@ -23750,7 +23736,7 @@ - + @@ -23762,14 +23748,14 @@ - + - + - - + + @@ -23825,37 +23811,37 @@ - + - + - + - + - + - + - + - + @@ -23875,16 +23861,16 @@ - + - + - + @@ -23901,7 +23887,7 @@ - + @@ -23945,22 +23931,22 @@ - - - + + + - + - + - + - - - + + + @@ -23994,13 +23980,13 @@ - + - + @@ -24008,8 +23994,8 @@ - - + + @@ -24096,14 +24082,14 @@ - + - + @@ -24186,7 +24172,7 @@ - + @@ -24311,22 +24297,22 @@ - + - + - + - + @@ -24400,26 +24386,26 @@ - - + + - + - - - - - + + + + + - - - - - + + + + + @@ -24462,38 +24448,38 @@ - - + + - + - + - + - + - + - - + + - - + + @@ -24518,7 +24504,7 @@ - + @@ -24530,7 +24516,7 @@ - + @@ -24539,7 +24525,7 @@ - + @@ -24552,7 +24538,7 @@ - + @@ -24628,27 +24614,27 @@ - - - + + + - - - + + + - + - - + + - - + + - + - + @@ -24686,14 +24672,14 @@ - - + + - - + + @@ -24701,20 +24687,20 @@ - + - + - + - + - + @@ -24723,12 +24709,12 @@ - + - + - + @@ -24738,8 +24724,8 @@ - - + + @@ -24747,25 +24733,25 @@ - - + + - + - - + + - + - + @@ -24783,7 +24769,7 @@ - + @@ -24792,7 +24778,7 @@ - + @@ -24818,7 +24804,7 @@ - + @@ -24839,7 +24825,7 @@ - + @@ -24890,28 +24876,28 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + @@ -24919,37 +24905,37 @@ - - + + - - + + - + - + - + - + - + - + @@ -24958,25 +24944,25 @@ - - + + - + - + - + - + @@ -24986,68 +24972,68 @@ - + - + - + - - + + - + - + - - + + - + - - - - - - + + + + + + - + - + - + - + - + - + @@ -25055,134 +25041,134 @@ - + - + - + - - - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - + + + + + + - - - - + + + + - - + + - - - - + + + + - - - - - + + + + + - + - + - + - + - + - + - + - - + + - + - + - + - + @@ -25228,24 +25214,24 @@ - - - - - - + + + + + - - + + + - - + + - - - + + + @@ -25258,27 +25244,27 @@ - + - + - + - + - - + + @@ -25301,11 +25287,11 @@ - - - + + + - + @@ -25314,7 +25300,7 @@ - + @@ -25328,8 +25314,8 @@ - - + + @@ -25340,22 +25326,22 @@ - - + + - + - + - - - - + + + + @@ -25372,54 +25358,54 @@ - + - + - + - + - + - + - - + + - + - - - - - - - - - - - + + + + + + + + + + + - - + + @@ -25428,10 +25414,10 @@ - + - + @@ -25502,31 +25488,31 @@ - + - + - + - + - + - + - + - + @@ -25535,100 +25521,100 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - + @@ -25636,7 +25622,7 @@ - + @@ -25741,35 +25727,35 @@ - - + + - + - + - + - - - - - + + + + + - + @@ -25778,7 +25764,7 @@ - + @@ -25824,7 +25810,7 @@ - + @@ -25866,7 +25852,7 @@ - + @@ -25876,8 +25862,8 @@ - - + + @@ -25951,9 +25937,9 @@ - + - + @@ -25988,21 +25974,21 @@ - + - + - - + + - - - - + + + + @@ -26016,8 +26002,8 @@ - - + + @@ -26031,7 +26017,7 @@ - + @@ -26039,17 +26025,17 @@ - - - - - - - - - + + + + + + + + + - + @@ -26106,13 +26092,22 @@ - - + + + + + + + + + + + - + @@ -26120,45 +26115,45 @@ - + - + - + - + - - - + + + - - + + - - + + - - - + + + - + - + @@ -26204,7 +26199,7 @@ - + @@ -26212,7 +26207,7 @@ - + @@ -26220,7 +26215,7 @@ - + @@ -26232,21 +26227,21 @@ - + - + - - - + + + - + @@ -26255,12 +26250,12 @@ - - - + + + - + @@ -26268,54 +26263,54 @@ - + - + - - + + - - - + + + - + - - + + - - + + - - + + - - + + @@ -26332,7 +26327,7 @@ - + @@ -26341,7 +26336,7 @@ - + @@ -26349,24 +26344,24 @@ - + - + - + - + - + - + @@ -26376,8 +26371,8 @@ - - + + @@ -26385,61 +26380,56 @@ - - - - + + - - - - - - + + + - + - + - + - + - + - + - + - - + + - + - + - + @@ -26514,85 +26504,85 @@ - - - + + + - - + + - - - + + + - + - + - - - + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - + + - - + + - - + + - - - - - - + + + + + + - - + + - - - + + + - + - + @@ -26604,16 +26594,16 @@ - + - - + + - + - + @@ -26628,24 +26618,24 @@ - - - - - - - - - - - - + + + + + + + + + + + + - - + + - - + + @@ -26687,13 +26677,13 @@ - + - - + + @@ -26718,21 +26708,21 @@ - + - + - + - + @@ -26741,7 +26731,7 @@ - + @@ -26755,7 +26745,7 @@ - + @@ -26764,7 +26754,7 @@ - + @@ -26810,12 +26800,12 @@ - + - + @@ -26832,19 +26822,19 @@ - + - + - + @@ -26876,32 +26866,32 @@ - - - - + + + + - - - - - + + + + + - - + + - + - + - - + + @@ -26909,9 +26899,9 @@ - - - + + + @@ -26923,8 +26913,8 @@ - - + + @@ -26933,169 +26923,169 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + + + - + - + - + - - - + + + - + - - + + - + - - - - - + + + + + @@ -27107,8 +27097,8 @@ - - + + @@ -27154,21 +27144,21 @@ - + - + - - - + + + @@ -27177,19 +27167,19 @@ - + - + - + @@ -27198,38 +27188,38 @@ - - + + - + - - - - + + + + - + - - + + - + - - - - - - + + + + + + @@ -27245,7 +27235,7 @@ - + @@ -27254,24 +27244,24 @@ - + - + - + - + - + @@ -27287,84 +27277,84 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + + + - + - + - + @@ -27382,13 +27372,13 @@ - + - + - + @@ -27400,16 +27390,16 @@ - + - + - - - - + + + + @@ -27459,7 +27449,7 @@ - + @@ -27493,7 +27483,7 @@ - + @@ -27502,22 +27492,22 @@ - - + + - - - - + + + + - + - + @@ -27526,34 +27516,34 @@ - + - + - + - + - + - + @@ -27611,17 +27601,17 @@ - - - + + + - + - - + + @@ -27634,8 +27624,8 @@ - - + + @@ -27647,23 +27637,23 @@ - - + + - + - + - + @@ -27681,11 +27671,11 @@ - + - + @@ -27695,40 +27685,40 @@ - + - + - + - - + + - + - - - - + + + + - - + + - - + + - + - + @@ -27740,25 +27730,25 @@ - - + + - + - + - + - + - + - + @@ -27766,7 +27756,7 @@ - + @@ -27774,59 +27764,59 @@ - + - + - + - + - - - - - + + + + + - + - + - - + + - + - + - - - + + + - - - - - - - + + + + + + + @@ -27939,7 +27929,7 @@ - + @@ -27992,13 +27982,13 @@ - + - - + + @@ -28021,7 +28011,7 @@ - + @@ -28030,7 +28020,7 @@ - + @@ -28053,7 +28043,7 @@ - + @@ -28371,7 +28361,7 @@ - + @@ -28380,8 +28370,8 @@ - - + + @@ -28459,19 +28449,19 @@ - + - - + + - - - - - + + + + + @@ -28490,8 +28480,8 @@ - - + + @@ -28549,11 +28539,11 @@ - + - + @@ -28566,29 +28556,41 @@ + + + - + + + + + + + + + + - + - + - + - + - + @@ -28678,11 +28680,11 @@ - + - + @@ -28690,14 +28692,14 @@ - + - + @@ -28711,7 +28713,7 @@ - + @@ -28722,8 +28724,8 @@ - - + + @@ -28734,8 +28736,8 @@ - - + + @@ -28747,8 +28749,8 @@ - - + + @@ -28771,145 +28773,145 @@ - - - - + + + + - - - - + + + + - - - - - + + + + + - - - + + + - - - - + + + + - - - - - - - - + + + + + + + + - - - - - - + + + + + + - - - + + + - - - - + + + + - - - - + + + + - - - - - - + + + + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - + + + - - - - + + + + - - - - - + + + + + - - + + - - - - + + + + - - - - - + + + + + - - - + + + - - - + + + - - + + @@ -28923,7 +28925,7 @@ - + @@ -28960,22 +28962,22 @@ - - - - + + + + - + - + - + @@ -29032,10 +29034,6 @@ - - - - @@ -29105,7 +29103,7 @@ - + @@ -29136,7 +29134,7 @@ - + @@ -29144,48 +29142,48 @@ - + - - - + + + - - + + - + - + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - + + + @@ -29206,47 +29204,47 @@ - + - + - + - + - - + + - + - + - + - + - + @@ -29273,43 +29271,43 @@ - + - + - + - + - + - - + + - + - + - + - + @@ -29337,12 +29335,12 @@ - + - + @@ -29366,7 +29364,7 @@ - + From 6947c05c11435b0d94087ed5df0f65549b9f1a95 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Mon, 30 Sep 2024 22:49:16 +0200 Subject: [PATCH 14/15] Incorporate AA Turner's doc update (https://github.com/AA-Turner/cpython/commit/d7acbbd02f9e269075d98b8a30ab6c289ad91764) --- Doc/library/gc.rst | 2 +- Doc/whatsnew/3.13.rst | 53 ------------------------------------------- 2 files changed, 1 insertion(+), 54 deletions(-) diff --git a/Doc/library/gc.rst b/Doc/library/gc.rst index 790dfdfd00b196..8ce850ba777e7c 100644 --- a/Doc/library/gc.rst +++ b/Doc/library/gc.rst @@ -42,7 +42,7 @@ The :mod:`gc` module provides the following functions: With no arguments, run a full collection. The optional argument *generation* may be an integer specifying which generation to collect (from 0 to 2). A - :exc:`ValueError` is raised if the generation number is invalid. The sum of + :exc:`ValueError` is raised if the generation number is invalid. The sum of collected objects and uncollectable objects is returned. The free lists maintained for a number of built-in types are cleared diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 05d99064db2486..b8124096e4a82a 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -501,17 +501,6 @@ are not tier 3 supported platforms, but will have best-effort support. .. seealso:: :pep:`730`, :pep:`738` -.. _whatsnew313-incremental-gc: - -Incremental garbage collection ------------------------------- - -The cycle garbage collector is now incremental. -This means that maximum pause times are reduced -by an order of magnitude or more for larger heaps. -(Contributed by Mark Shannon in :gh:`108362`.) - - Other Language Changes ====================== @@ -908,36 +897,6 @@ fractions (Contributed by Mark Dickinson in :gh:`111320`.) -gc --- - -The cyclic garbage collector is now incremental, -which changes the meaning of the results of -:meth:`~gc.get_threshold` and :meth:`~gc.set_threshold` -as well as :meth:`~gc.get_count` and :meth:`~gc.get_stats`. - -* For backwards compatibility, :meth:`~gc.get_threshold` continues to return - a three-item tuple. - The first value is the threshold for young collections, as before; - the second value determines the rate at which the old collection is scanned - (the default is 10, and higher values mean that the old collection - is scanned more slowly). - The third value is meaningless and is always zero. - -* :meth:`~gc.set_threshold` ignores any items after the second. - -* :meth:`~gc.get_count` and :meth:`~gc.get_stats` continue to return - the same format of results. - The only difference is that instead of the results referring to - the young, aging and old generations, - the results refer to the young generation - and the aging and collecting spaces of the old generation. - -In summary, code that attempted to manipulate the behavior of the cycle GC -may not work exactly as intended, but it is very unlikely to be harmful. -All other code will work just fine. - - glob ---- @@ -1502,11 +1461,6 @@ zipimport Optimizations ============= -* The new :ref:`incremental garbage collector ` - means that maximum pause times are reduced - by an order of magnitude or more for larger heaps. - (Contributed by Mark Shannon in :gh:`108362`.) - * Several standard library modules have had their import times significantly improved. For example, the import time of the :mod:`typing` module @@ -2619,13 +2573,6 @@ Changes in the Python API Wrap it in :func:`staticmethod` if you want to preserve the old behavior. (Contributed by Serhiy Storchaka in :gh:`121027`.) -* The :ref:`garbage collector is now incremental `, - which means that the behavior of :func:`gc.collect` changes slightly: - - * ``gc.collect(1)``: Performs an increment of garbage collection, - rather than collecting generation 1. - * Other calls to :func:`!gc.collect` are unchanged. - * An :exc:`OSError` is now raised by :func:`getpass.getuser` for any failure to retrieve a username, instead of :exc:`ImportError` on non-Unix platforms From 8504cc023107cf6a0c51558a1e1ae1d9889ec192 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 21:02:15 +0000 Subject: [PATCH 15/15] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2024-09-30-21-02-10.gh-issue-124567.tv_B_C.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-09-30-21-02-10.gh-issue-124567.tv_B_C.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-09-30-21-02-10.gh-issue-124567.tv_B_C.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-09-30-21-02-10.gh-issue-124567.tv_B_C.rst new file mode 100644 index 00000000000000..96091972a499ae --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-09-30-21-02-10.gh-issue-124567.tv_B_C.rst @@ -0,0 +1 @@ +Revert the incremental GC (in 3.13), since it's not clear the benefits outweigh the costs at this point.