@@ -318,13 +318,13 @@ static Py_ssize_t
318318_Py_GC_REFCNT (PyObject * op )
319319{
320320 Py_ssize_t local , shared ;
321- int immortal ;
321+ int immortal , deferred ;
322322
323- _PyRef_UnpackLocal (op -> ob_ref_local , & local , & immortal );
323+ _PyRef_UnpackLocal (op -> ob_ref_local , & local , & immortal , & deferred );
324324 _PyRef_UnpackShared (op -> ob_ref_shared , & shared , NULL , NULL );
325325 assert (!immortal );
326326
327- return local + shared ;
327+ return local + shared - deferred ;
328328}
329329
330330typedef int (gc_visit_fn )(PyGC_Head * gc , void * arg );
@@ -682,9 +682,35 @@ find_dead_shared_keys(_PyObjectQueue **queue, int *num_unmarked)
682682 }
683683}
684684
685+ static void
686+ merge_refcount (PyObject * op , Py_ssize_t extra )
687+ {
688+ Py_ssize_t local_refcount , shared_refcount ;
689+ int immortal , deferred ;
690+
691+ assert (_PyRuntime .stop_the_world );
692+
693+ _PyRef_UnpackLocal (op -> ob_ref_local , & local_refcount , & immortal , & deferred );
694+ _PyRef_UnpackShared (op -> ob_ref_shared , & shared_refcount , NULL , NULL );
695+ assert (!immortal && "immortal objects should not be in garbage" );
696+
697+ Py_ssize_t refcount = local_refcount + shared_refcount ;
698+ refcount += extra ;
699+ refcount -= deferred ;
700+
701+ #ifdef Py_REF_DEBUG
702+ _Py_IncRefTotalN (extra );
703+ #endif
704+
705+ op -> ob_tid = 0 ;
706+ op -> ob_ref_local = 0 ;
707+ op -> ob_ref_shared = _Py_REF_PACK_SHARED (refcount , _Py_REF_MERGED );
708+ }
709+
685710struct update_refs_args {
686711 PyGC_Head * list ;
687712 int split_keys_marked ;
713+ _PyGC_Reason gc_reason ;
688714};
689715
690716// Compute the number of external references to objects in the heap
@@ -730,6 +756,15 @@ update_refs(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size
730756 }
731757 }
732758
759+ if (arg -> gc_reason == GC_REASON_SHUTDOWN ) {
760+ if (_PyObject_HasDeferredRefcount (op )) {
761+ // Disable deferred reference counting when we're shutting down.
762+ // This is useful for interp->sysdict because the last reference
763+ // to it is cleared after the last GC cycle.
764+ merge_refcount (op , 0 );
765+ }
766+ }
767+
733768 // Add the actual refcount to gc_refs.
734769 Py_ssize_t refcount = _Py_GC_REFCNT (op );
735770 _PyObject_ASSERT (op , refcount >= 0 );
@@ -1008,23 +1043,7 @@ move_legacy_finalizer_reachable(PyGC_Head *finalizers)
10081043static void
10091044incref_merge (PyObject * op )
10101045{
1011- Py_ssize_t local_refcount , shared_refcount ;
1012- int immortal ;
1013-
1014- assert (_PyRuntime .stop_the_world );
1015-
1016- #ifdef Py_REF_DEBUG
1017- _Py_IncRefTotal ();
1018- #endif
1019-
1020- _PyRef_UnpackLocal (op -> ob_ref_local , & local_refcount , & immortal );
1021- _PyRef_UnpackShared (op -> ob_ref_shared , & shared_refcount , NULL , NULL );
1022- assert (!immortal && "immortal objects should not be in garbage" );
1023-
1024- Py_ssize_t refcount = local_refcount + shared_refcount + 1 ;
1025- op -> ob_tid = 0 ;
1026- op -> ob_ref_local = 0 ;
1027- op -> ob_ref_shared = _Py_REF_PACK_SHARED (refcount , _Py_REF_MERGED );
1046+ merge_refcount (op , 1 );
10281047}
10291048
10301049/* Subtracts one from the refcount field. */
@@ -1594,7 +1613,11 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason)
15941613 validate_tracked_heap (_PyGC_PREV_MASK |_PyGC_PREV_MASK_UNREACHABLE , 0 );
15951614
15961615 gc_list_init (& young );
1597- struct update_refs_args args = { .list = & young , .split_keys_marked = 0 };
1616+ struct update_refs_args args = {
1617+ .list = & young ,
1618+ .split_keys_marked = 0 ,
1619+ .gc_reason = reason ,
1620+ };
15981621 visit_heaps2 (mi_heap_tag_gc , update_refs , & args );
15991622
16001623 _PyObjectQueue * dead_keys = NULL ;
0 commit comments