@@ -2002,7 +2002,9 @@ gc_should_collect(GCState *gcstate)
20022002 int count = _Py_atomic_load_int_relaxed (& gcstate -> young .count );
20032003 int threshold = gcstate -> young .threshold ;
20042004 int gc_enabled = _Py_atomic_load_int_relaxed (& gcstate -> enabled );
2005- if (count <= threshold || threshold == 0 || !gc_enabled ) {
2005+ int pause_count = _Py_atomic_load_int_relaxed (
2006+ & gcstate -> automatic_collection_pause_count );
2007+ if (count <= threshold || threshold == 0 || !gc_enabled || pause_count ) {
20062008 return false;
20072009 }
20082010 if (gcstate -> old [0 ].threshold == 0 ) {
@@ -2065,11 +2067,21 @@ record_deallocation(PyThreadState *tstate)
20652067 }
20662068}
20672069
2068- static void
2069- gc_collect_internal (PyInterpreterState * interp , struct collection_state * state , int generation )
2070+ static bool
2071+ gc_collect_internal (PyInterpreterState * interp ,
2072+ struct collection_state * state , int generation )
20702073{
20712074 _PyEval_StopTheWorld (interp );
20722075
2076+ // A concurrent deferral may begin after this collection has emitted its
2077+ // start notification, but it must take effect before any heap traversal.
2078+ if (state -> reason == _Py_GC_REASON_HEAP &&
2079+ _Py_atomic_load_int (
2080+ & state -> gcstate -> automatic_collection_pause_count )) {
2081+ _PyEval_StartTheWorld (interp );
2082+ return false;
2083+ }
2084+
20732085 // update collection and allocation counters
20742086 if (generation + 1 < NUM_GENERATIONS ) {
20752087 state -> gcstate -> old [generation ].count += 1 ;
@@ -2114,7 +2126,7 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
21142126 if (err < 0 ) {
21152127 _PyEval_StartTheWorld (interp );
21162128 PyErr_NoMemory ();
2117- return ;
2129+ return true ;
21182130 }
21192131 }
21202132 #endif
@@ -2124,7 +2136,7 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
21242136 if (err < 0 ) {
21252137 _PyEval_StartTheWorld (interp );
21262138 PyErr_NoMemory ();
2127- return ;
2139+ return true ;
21282140 }
21292141
21302142#ifdef GC_DEBUG
@@ -2171,7 +2183,7 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
21712183 cleanup_worklist (& state -> wrcb_to_call );
21722184 cleanup_worklist (& state -> objs_to_decref );
21732185 PyErr_NoMemory ();
2174- return ;
2186+ return true ;
21752187 }
21762188
21772189 // Call tp_clear on objects in the unreachable set. This will cause
@@ -2181,6 +2193,7 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
21812193
21822194 // Append objects with legacy finalizers to the "gc.garbage" list.
21832195 handle_legacy_finalizers (state );
2196+ return true;
21842197}
21852198
21862199static struct gc_generation_stats *
@@ -2258,7 +2271,17 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason)
22582271 .reason = reason ,
22592272 };
22602273
2261- gc_collect_internal (interp , & state , generation );
2274+ if (!gc_collect_internal (interp , & state , generation )) {
2275+ if (PyDTrace_GC_DONE_ENABLED ()) {
2276+ PyDTrace_GC_DONE (0 );
2277+ }
2278+ if (reason != _Py_GC_REASON_SHUTDOWN ) {
2279+ invoke_gc_callback (tstate , "stop" , generation , 0 , 0 , 0 , 0.0 );
2280+ }
2281+ gcstate -> frame = NULL ;
2282+ _Py_atomic_store_int (& gcstate -> collecting , 0 );
2283+ return 0 ;
2284+ }
22622285
22632286 m = state .collected ;
22642287 n = state .uncollectable ;
@@ -2549,6 +2572,26 @@ PyGC_IsEnabled(void)
25492572 return _Py_atomic_load_int_relaxed (& gcstate -> enabled );
25502573}
25512574
2575+ void
2576+ _PyGC_DeferAutomaticCollection (PyThreadState * tstate )
2577+ {
2578+ GCState * gcstate = & tstate -> interp -> gc ;
2579+ int previous = _Py_atomic_add_int (
2580+ & gcstate -> automatic_collection_pause_count , 1 );
2581+ (void )previous ;
2582+ assert (previous >= 0 );
2583+ }
2584+
2585+ void
2586+ _PyGC_ResumeAutomaticCollection (PyThreadState * tstate )
2587+ {
2588+ GCState * gcstate = & tstate -> interp -> gc ;
2589+ int previous = _Py_atomic_add_int (
2590+ & gcstate -> automatic_collection_pause_count , -1 );
2591+ (void )previous ;
2592+ assert (previous > 0 );
2593+ }
2594+
25522595/* Public API to invoke gc.collect() from C */
25532596Py_ssize_t
25542597PyGC_Collect (void )
0 commit comments