-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathgcmodule.c
2141 lines (1815 loc) · 61.1 KB
/
gcmodule.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
Reference Cycle Garbage Collection
==================================
Neil Schemenauer <[email protected]>
Based on a post on the python-dev list. Ideas from Guido van Rossum,
Eric Tiedemann, and various others.
http://www.arctrix.com/nas/python/gc/
The following mailing list threads provide a historical perspective on
the design of this module. Note that a fair amount of refinement has
occurred since those discussions.
http://mail.python.org/pipermail/python-dev/2000-March/002385.html
http://mail.python.org/pipermail/python-dev/2000-March/002434.html
http://mail.python.org/pipermail/python-dev/2000-March/002497.html
For a highlevel view of the collection process, read the collect
function.
*/
#include "Python.h"
#include "pycore_context.h"
#include "pycore_dict.h"
#include "pycore_initconfig.h"
#include "pycore_interp.h" // PyInterpreterState.gc
#include "pycore_object.h"
#include "pycore_pyerrors.h"
#include "pycore_pymem.h"
#include "pycore_pystate.h"
#include "pycore_refcnt.h"
#include "pycore_qsbr.h"
#include "pycore_gc.h"
#include "frameobject.h" /* for PyFrame_ClearFreeList */
#include "pydtrace.h"
#include "mimalloc.h"
#include "mimalloc-internal.h"
typedef struct _gc_runtime_state GCState;
/*[clinic input]
module gc
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=b5c9690ecc842d79]*/
#ifdef Py_DEBUG
# define GC_DEBUG
#endif
typedef enum {
/* GC was triggered by heap allocation */
GC_REASON_HEAP,
/* GC was called due to shutdown */
GC_REASON_SHUTDOWN,
/* GC was called via gc.collect() or PyGC_Collect */
GC_REASON_MANUAL
} _PyGC_Reason;
static void
merge_refcount(PyObject *op, Py_ssize_t extra);
static inline int
gc_is_unreachable(PyObject *op)
{
return (op->ob_gc_bits & _PyGC_UNREACHABLE) != 0;
}
static void
gc_set_unreachable(PyObject *op)
{
if (!gc_is_unreachable(op)) {
op->ob_gc_bits |= _PyGC_UNREACHABLE;
// We're going to use ob_tid to store the difference between the refcount
// and the number of incoming references.
op->ob_tid = 0;
}
}
static void
gc_clear_unreachable(PyObject *op)
{
op->ob_gc_bits &= ~_PyGC_UNREACHABLE;
}
static void
gc_restore_tid(PyObject *op)
{
mi_segment_t *segment = _mi_ptr_segment(op);
if (_Py_REF_IS_MERGED(op->ob_ref_shared)) {
op->ob_tid = 0;
}
else {
// NOTE: may change ob_tid if the object was re-initialized by
// a different thread or its segment was abandoned and reclaimed.
op->ob_tid = segment->thread_id;
// The segment thread id might be zero, in which case we should
// ensure the refcounts are now merged.
if (op->ob_tid == 0) {
merge_refcount(op, 0);
}
}
}
static inline Py_ssize_t
gc_get_refs(PyObject *op)
{
return (Py_ssize_t)op->ob_tid;
}
static inline void
gc_add_refs(PyObject *op, Py_ssize_t refs)
{
assert(_PyObject_GC_IS_TRACKED(op));
op->ob_tid += refs;
}
static inline void
gc_decref(PyObject *op)
{
op->ob_tid -= 1;
}
/* set for debugging information */
#define DEBUG_STATS (1<<0) /* print collection statistics */
#define DEBUG_COLLECTABLE (1<<1) /* print collectable objects */
#define DEBUG_UNCOLLECTABLE (1<<2) /* print uncollectable objects */
#define DEBUG_SAVEALL (1<<5) /* save all garbage in gc.garbage */
#define DEBUG_LEAK DEBUG_COLLECTABLE | \
DEBUG_UNCOLLECTABLE | \
DEBUG_SAVEALL
static GCState *
get_gc_state(void)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
return &interp->gc;
}
void
_PyGC_InitState(GCState *gcstate)
{
gcstate->enabled = 1; /* automatic collection enabled? */
gcstate->gc_live = 0;
gcstate->gc_threshold = 7000;
gcstate->gc_scale = 100;
const char* scale_str = _Py_GetEnv(1, "PYTHONGC");
if (scale_str) {
(void)_Py_str_to_int(scale_str, &gcstate->gc_scale);
}
}
PyStatus
_PyGC_Init(PyInterpreterState *interp)
{
GCState *gcstate = &interp->gc;
gcstate->garbage = PyList_New(0);
if (gcstate->garbage == NULL) {
return _PyStatus_NO_MEMORY();
}
gcstate->callbacks = PyList_New(0);
if (gcstate->callbacks == NULL) {
return _PyStatus_NO_MEMORY();
}
return _PyStatus_OK();
}
/*** list functions ***/
static Py_ssize_t
_Py_GC_REFCNT(PyObject *op)
{
Py_ssize_t local, shared;
int immortal, deferred;
_PyRef_UnpackLocal(op->ob_ref_local, &local, &immortal, &deferred);
_PyRef_UnpackShared(op->ob_ref_shared, &shared, NULL, NULL);
assert(!immortal);
return local + shared - deferred;
}
struct visitor_args {
size_t offset;
};
static bool
visit_heaps(mi_block_visit_fun *visitor, struct visitor_args *arg)
{
_PyRuntimeState *runtime = &_PyRuntime;
PyThreadState *t;
bool ret = true;
HEAD_LOCK(runtime);
int offsets[] = {
[mi_heap_tag_gc] = 0,
[mi_heap_tag_gc_pre] = _PyGC_PREHEADER_SIZE,
};
if (_PyMem_DebugEnabled()) {
offsets[mi_heap_tag_gc] += 2 * sizeof(size_t);
offsets[mi_heap_tag_gc_pre] += 2 * sizeof(size_t);
}
for_each_thread(t) {
if (!t->heaps) continue;
for (mi_heap_tag_t tag = mi_heap_tag_gc; tag <= mi_heap_tag_gc_pre; tag++) {
arg->offset = offsets[tag];
mi_heap_t *heap = &t->heaps[tag];
if (!heap->visited) {
if (!mi_heap_visit_blocks(heap, true, visitor, arg)) {
ret = false;
goto exit;
}
heap->visited = true;
}
heap->visited = true;
}
}
for (mi_heap_tag_t tag = mi_heap_tag_gc; tag <= mi_heap_tag_gc_pre; tag++) {
arg->offset = offsets[tag];
if (!_mi_abandoned_visit_blocks(tag, true, visitor, arg)) {
ret = false;
goto exit;
}
}
exit:
for_each_thread(t) {
if (t->heaps) {
for (mi_heap_tag_t tag = mi_heap_tag_gc; tag <= mi_heap_tag_gc_pre; tag++) {
mi_heap_t *heap = &t->heaps[tag];
if (heap) {
heap->visited = false;
}
}
}
}
HEAD_UNLOCK(runtime);
return ret;
}
struct find_object_args {
struct visitor_args base;
PyObject *op;
int found;
};
#define VISITOR_BEGIN(block, arg) \
if (block == NULL) return true; \
PyObject *op = (PyObject *)((char *)block + *(size_t *)arg)
static bool
find_object_visitor(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* arg)
{
VISITOR_BEGIN(block, arg);
struct find_object_args *args = (struct find_object_args *)arg;
if (op == args->op) {
args->found = 1;
}
return true;
}
int
_PyGC_find_object(PyObject *op)
{
struct find_object_args args;
args.op = op;
args.found = 0;
visit_heaps(find_object_visitor, &args.base);
return args.found;
}
#ifdef GC_DEBUG
static bool
validate_refcount_visitor(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* args)
{
VISITOR_BEGIN(block, args);
if (_PyObject_GC_IS_TRACKED(op) && !_PyObject_IS_IMMORTAL(op)) {
assert(_Py_GC_REFCNT(op) >= 0);
// assert((op->ob_gc_bits & _PyGC_MASK_TID_REFCOUNT) == 0);
}
return true;
}
static void
validate_refcount(void)
{
struct visitor_args args;
visit_heaps(validate_refcount_visitor, &args);
}
#else
#define validate_refcount() do{}while(0)
#endif
static bool
reset_heap_visitor(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* args)
{
VISITOR_BEGIN(block, args);
if (!_PyObject_GC_IS_TRACKED(op)) {
return true;
}
op->ob_gc_bits = 0;
return true;
}
void
_PyGC_ResetHeap(void)
{
// NOTE: _PyGC_Initialize may be called multiple times. For example,
// _test_embed triggers multiple GC initializations, including some
// after _Py_Initialize failures. Since _Py_Initialize clears _PyRuntime
// we have no choice but to leak all PyObjects.
// TODO(sgross): should we drop mi_heap here instead?
struct visitor_args args;
visit_heaps(reset_heap_visitor, &args);
}
static bool
immortalize_heap_visitor(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* args)
{
VISITOR_BEGIN(block, args);
Py_ssize_t refcount;
int immortal, deferred;
_PyRef_UnpackLocal(op->ob_ref_local, &refcount, &immortal, &deferred);
if (deferred) {
_PyObject_SetImmortal(op);
if (_PyObject_GC_IS_TRACKED(op)) {
_PyObject_GC_UNTRACK(op);
}
}
return true;
}
void
_PyGC_DeferredToImmortal(void)
{
struct visitor_args args;
visit_heaps(immortalize_heap_visitor, &args);
}
/* Subtracts incoming references. */
static int
visit_decref(PyObject *op, void *arg)
{
if (_PyObject_GC_IS_TRACKED(op)) {
// If update_refs hasn't reached this object yet, mark it
// as (tentatively) unreachable and initialize ob_tid to zero.
gc_set_unreachable(op);
gc_decref(op);
}
return 0;
}
static void
find_dead_shared_keys(_PyObjectQueue **queue, int *num_unmarked)
{
PyInterpreterState *interp = _PyRuntime.interpreters.head;
while (interp) {
struct _Py_dict_state *dict_state = &interp->dict_state;
PyDictSharedKeysObject **prev_nextptr = &dict_state->tracked_shared_keys;
PyDictSharedKeysObject *keys = dict_state->tracked_shared_keys;
while (keys) {
assert(keys->tracked);
PyDictSharedKeysObject *next = keys->next;
if (keys->marked) {
keys->marked = 0;
prev_nextptr = &keys->next;
*num_unmarked += 1;
}
else {
*prev_nextptr = next;
// FIXME: bad cast
_PyObjectQueue_Push(queue, (PyObject *)keys);
}
keys = next;
}
interp = interp->next;
}
}
static void
merge_refcount(PyObject *op, Py_ssize_t extra)
{
Py_ssize_t local_refcount, shared_refcount;
int immortal, deferred;
assert(_PyRuntime.stop_the_world);
_PyRef_UnpackLocal(op->ob_ref_local, &local_refcount, &immortal, &deferred);
_PyRef_UnpackShared(op->ob_ref_shared, &shared_refcount, NULL, NULL);
assert(!immortal && "immortal objects should not be in garbage");
Py_ssize_t refcount = local_refcount + shared_refcount;
refcount += extra;
refcount -= deferred;
#ifdef Py_REF_DEBUG
_Py_IncRefTotalN(extra);
#endif
op->ob_ref_local = 0;
op->ob_ref_shared = _Py_REF_PACK_SHARED(refcount, _Py_REF_MERGED);
}
struct update_refs_args {
struct visitor_args base;
GCState *gcstate;
int split_keys_marked;
_PyGC_Reason gc_reason;
};
// Compute the number of external references to objects in the heap
// by subtracting internal references from the refcount.
static bool
update_refs(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* args)
{
VISITOR_BEGIN(block, args);
struct update_refs_args *arg = (struct update_refs_args *)args;
if (PyDict_CheckExact(op)) {
PyDictObject *mp = (PyDictObject *)op;
if (mp->ma_keys && mp->ma_keys->dk_kind == DICT_KEYS_SPLIT) {
PyDictSharedKeysObject *shared = DK_AS_SPLIT(mp->ma_keys);
if (shared->tracked) {
shared->marked = 1;
arg->split_keys_marked++;
}
}
}
if (!_PyObject_GC_IS_TRACKED(op)) {
return true;
};
if (_PyObject_IS_IMMORTAL(op)) {
_PyObject_GC_UNTRACK(op);
if (gc_is_unreachable(op)) {
gc_clear_unreachable(op);
_PyObject_SetImmortal(op);
}
return true;
}
if (PyTuple_CheckExact(op)) {
_PyTuple_MaybeUntrack(op);
if (!_PyObject_GC_IS_TRACKED(op)) {
if (gc_is_unreachable(op)) {
gc_restore_tid(op);
gc_clear_unreachable(op);
}
return true;
}
}
else if (PyDict_CheckExact(op)) {
_PyDict_MaybeUntrack(op);
if (!_PyObject_GC_IS_TRACKED(op)) {
if (gc_is_unreachable(op)) {
gc_restore_tid(op);
gc_clear_unreachable(op);
}
return true;
}
}
if (arg->gc_reason == GC_REASON_SHUTDOWN) {
if (_PyObject_HasDeferredRefcount(op)) {
// Disable deferred reference counting when we're shutting down.
// This is useful for interp->sysdict because the last reference
// to it is cleared after the last GC cycle.
merge_refcount(op, 0);
}
}
// Add the actual refcount to gc_refs.
Py_ssize_t refcount = _Py_GC_REFCNT(op);
_PyObject_ASSERT(op, refcount >= 0);
gc_set_unreachable(op);
gc_add_refs(op, refcount);
// Subtract internal references from gc_refs. Objects with gc_refs > 0
// are directly reachable from outside containers, and so can't be
// collected.
Py_TYPE(op)->tp_traverse(op, visit_decref, NULL);
return true;
}
static void
find_gc_roots(GCState *gcstate, _PyGC_Reason reason, Py_ssize_t *split_keys_marked)
{
struct update_refs_args args = {
.gcstate = gcstate,
.split_keys_marked = 0,
.gc_reason = reason,
};
visit_heaps(update_refs, &args.base);
*split_keys_marked = args.split_keys_marked;
}
/* Return true if object has a pre-PEP 442 finalization method. */
static int
has_legacy_finalizer(PyObject *op)
{
return Py_TYPE(op)->tp_del != NULL;
}
/* Adds one to the refcount and merges the local and shared fields. */
static void
incref_merge(PyObject *op)
{
merge_refcount(op, 1);
op->ob_tid = 0;
}
static void
debug_cycle(const char *msg, PyObject *op)
{
PySys_FormatStderr("gc: %s <%s %p>\n",
msg, Py_TYPE(op)->tp_name, op);
}
/* Clear all weakrefs to unreachable objects, and if such a weakref has a
* callback, invoke it if necessary. Note that it's possible for such
* weakrefs to be outside the unreachable set -- indeed, those are precisely
* the weakrefs whose callbacks must be invoked. See gc_weakref.txt for
* overview & some details. Some weakrefs with callbacks may be reclaimed
* directly by this routine; the number reclaimed is the return value. Other
* weakrefs with callbacks may be moved into the `old` generation. Objects
* moved into `old` have gc_refs set to GC_REACHABLE; the objects remaining in
* unreachable are left at GC_TENTATIVELY_UNREACHABLE. When this returns,
* no object in `unreachable` is weakly referenced anymore.
*/
static void
clear_weakrefs(GCState *gcstate)
{
/* Clear all weakrefs to the objects in unreachable. If such a weakref
* also has a callback, move it into `wrcb_to_call` if the callback
* needs to be invoked. Note that we cannot invoke any callbacks until
* all weakrefs to unreachable objects are cleared, lest the callback
* resurrect an unreachable object via a still-active weakref. We
* make another pass over wrcb_to_call, invoking callbacks, after this
* pass completes.
*/
_PyObjectQueue *q = gcstate->gc_unreachable;
while (q != NULL) {
for (Py_ssize_t i = 0, n = q->n; i != n; i++) {
PyObject *op = q->objs[i];
/* Add one to the refcount to prevent deallocation while we're holding
* on to it in a list. */
incref_merge(op);
/* Print debugging information. */
if (gcstate->debug & DEBUG_COLLECTABLE) {
debug_cycle("collectable", op);
}
if (PyWeakref_Check(op)) {
/* A weakref inside the unreachable set must be cleared. If we
* allow its callback to execute inside delete_garbage(), it
* could expose objects that have tp_clear already called on
* them. Or, it could resurrect unreachable objects. One way
* this can happen is if some container objects do not implement
* tp_traverse. Then, wr_object can be outside the unreachable
* set but can be deallocated as a result of breaking the
* reference cycle. If we don't clear the weakref, the callback
* will run and potentially cause a crash. See bpo-38006 for
* one example.
*/
_PyWeakref_DetachRef((PyWeakReference *)op);
}
if (! _PyType_SUPPORTS_WEAKREFS(Py_TYPE(op)))
continue;
/* It supports weakrefs. Does it have any?
*
* This is never triggered for static types so we can avoid the
* (slightly) more costly _PyObject_GET_WEAKREFS_LISTPTR().
*/
PyWeakrefBase *ctrl = (PyWeakrefBase *)_PyObject_GET_WEAKREF_CONTROL(op);
if (!ctrl)
continue;
PyWeakrefBase *ref;
for (ref = ctrl->wr_next; ref != ctrl; ref = ref->wr_next) {
PyWeakReference *wr = (PyWeakReference *)ref;
if (wr->wr_callback == NULL) {
/* no callback */
continue;
}
/* Headache time. `op` is going away, and is weakly referenced by
* `wr`, which has a callback. Should the callback be invoked? If wr
* is also trash, no:
*
* 1. There's no need to call it. The object and the weakref are
* both going away, so it's legitimate to pretend the weakref is
* going away first. The user has to ensure a weakref outlives its
* referent if they want a guarantee that the wr callback will get
* invoked.
*
* 2. It may be catastrophic to call it. If the callback is also in
* cyclic trash (CT), then although the CT is unreachable from
* outside the current generation, CT may be reachable from the
* callback. Then the callback could resurrect insane objects.
*
* Since the callback is never needed and may be unsafe in this case,
* wr is simply left in the unreachable set. Note that because we
* already called _PyWeakref_ClearRef(wr), its callback will never
* trigger.
*
* OTOH, if wr isn't part of CT, we should invoke the callback: the
* weakref outlived the trash. Note that since wr isn't CT in this
* case, its callback can't be CT either -- wr acted as an external
* root to this generation, and therefore its callback did too. So
* nothing in CT is reachable from the callback either, so it's hard
* to imagine how calling it later could create a problem for us. wr
* is moved to wrcb_to_call in this case.
*/
if (gc_is_unreachable((PyObject *)wr)) {
/* it should already have been cleared above */
// assert(wr->wr_object == Py_None);
continue;
}
/* Create a new reference so that wr can't go away
* before we can process it again.
*/
Py_INCREF(wr);
_PyObjectQueue_Push(&gcstate->gc_wrcb_to_call, (PyObject *)wr);
}
/* Clear the root weakref but does not invoke any callbacks.
* Other weak references reference this object
*/
_PyObject_ClearWeakRefsFromGC(op);
}
q = q->prev;
}
}
static void
call_weakref_callbacks(GCState *gcstate)
{
/* Invoke the callbacks we decided to honor. It's safe to invoke them
* because they can't reference unreachable objects.
*/
PyObject *op;
while ((op = _PyObjectQueue_Pop(&gcstate->gc_wrcb_to_call))) {
_PyObject_ASSERT(op, PyWeakref_Check(op));
PyWeakReference *wr = (PyWeakReference *)op;
PyObject *callback = wr->wr_callback;
_PyObject_ASSERT(op, callback != NULL);
/* copy-paste of weakrefobject.c's handle_callback() */
PyObject *temp = PyObject_CallOneArg(callback, (PyObject *)wr);
if (temp == NULL)
PyErr_WriteUnraisable(callback);
else
Py_DECREF(temp);
/* Give up the reference we created in the first pass. When
* op's refcount hits 0 (which it may or may not do right now),
* op's tp_dealloc will decref op->wr_callback too. Note
* that the refcount probably will hit 0 now, and because this
* weakref was reachable to begin with, gc didn't already
* add it to its count of freed objects. Example: a reachable
* weak value dict maps some key to this reachable weakref.
* The callback removes this key->weakref mapping from the
* dict, leaving no other references to the weakref (excepting
* ours).
*/
Py_DECREF(op);
}
}
static void
merge_queued_objects(_PyObjectQueue **to_dealloc_ptr)
{
HEAD_LOCK(&_PyRuntime);
PyThreadState *t;
for_each_thread(t) {
_Py_queue_process_gc(t, to_dealloc_ptr);
}
HEAD_UNLOCK(&_PyRuntime);
}
static void
dealloc_non_gc(_PyObjectQueue **queue_ptr)
{
for (;;) {
PyObject *op = _PyObjectQueue_Pop(queue_ptr);
if (op == NULL) {
break;
}
_Py_Dealloc(op);
}
assert(*queue_ptr == NULL);
}
static void
free_dict_keys(_PyObjectQueue **queue_ptr)
{
for (;;) {
PyDictSharedKeysObject *keys = (PyDictSharedKeysObject *)_PyObjectQueue_Pop(queue_ptr);
if (keys == NULL) {
break;
}
PyMem_Free(keys);
}
assert(*queue_ptr == NULL);
}
/* Run first-time finalizers (if any) on all the objects in collectable.
* Note that this may remove some (or even all) of the objects from the
* list, due to refcounts falling to 0.
*/
static void
finalize_garbage(PyThreadState *tstate, GCState *gcstate)
{
_PyObjectQueue *q = gcstate->gc_unreachable;
while (q != NULL) {
for (Py_ssize_t i = 0, n = q->n; i != n; i++) {
PyObject *op = q->objs[i];
destructor finalize;
if (!_PyGC_FINALIZED(op) &&
(finalize = Py_TYPE(op)->tp_finalize) != NULL) {
_PyGC_SET_FINALIZED(op);
finalize(op);
assert(!_PyErr_Occurred(tstate));
}
}
q = q->prev;
}
}
/* Break reference cycles by clearing the containers involved. This is
* tricky business as the lists can be changing and we don't know which
* objects may be freed. It is possible I screwed something up here.
*/
static void
delete_garbage(PyThreadState *tstate, GCState *gcstate)
{
assert(!_PyErr_Occurred(tstate));
PyObject *op;
while ((op = _PyObjectQueue_Pop(&gcstate->gc_unreachable))) {
if (gc_is_unreachable(op)) {
gcstate->gc_collected++;
op->ob_gc_bits -= _PyGC_UNREACHABLE;
_PyObject_ASSERT_WITH_MSG(op, _Py_GC_REFCNT(op) > 0,
"refcount is too small");
if (gcstate->debug & DEBUG_SAVEALL) {
assert(gcstate->garbage != NULL);
if (PyList_Append(gcstate->garbage, op) < 0) {
_PyErr_Clear(tstate);
}
}
else {
inquiry clear;
if ((clear = Py_TYPE(op)->tp_clear) != NULL) {
(void) clear(op);
if (_PyErr_Occurred(tstate)) {
_PyErr_WriteUnraisableMsg("in tp_clear of",
(PyObject*)Py_TYPE(op));
}
}
}
}
Py_DECREF(op);
}
}
static void
clear_freelists(PyThreadState *tstate)
{
_PyTuple_ClearFreeList(tstate);
_PyFloat_ClearFreeList(tstate);
_PyList_ClearFreeList(tstate);
_PyDict_ClearFreeList(tstate);
_PyAsyncGen_ClearFreeLists(tstate);
_PyContext_ClearFreeList(tstate);
}
/* Clear all free lists
* All free lists are cleared during the collection of the highest generation.
* Allocated items in the free list may keep a pymalloc arena occupied.
* Clearing the free lists may give back memory to the OS earlier.
*/
static void
clear_all_freelists(PyInterpreterState *interp)
{
HEAD_LOCK(&_PyRuntime);
PyThreadState *tstate = interp->threads.head;
while (tstate != NULL) {
clear_freelists(tstate);
tstate = tstate->next;
}
HEAD_UNLOCK(&_PyRuntime);
}
static int
visit_reachable_heap(PyObject *op, GCState *gcstate)
{
if (gc_is_unreachable(op)) {
assert(_PyObject_GC_IS_TRACKED(op));
gc_clear_unreachable(op);
op->ob_tid = 0; // set gc refcount to zero
_PyObjectQueue_Push(&gcstate->gc_work, op);
}
return 0;
}
struct visit_heap_args {
struct visitor_args base;
GCState *gcstate;
};
static bool
mark_heap_visitor(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* args)
{
VISITOR_BEGIN(block, args);
if (gc_get_refs(op) == 0 || !gc_is_unreachable(op)) {
return true;
}
// Object is reachable but currently marked as unreachable.
// Mark it as reachable and traverse its pointers to find
// any other object that may be directly reachable from it.
_PyObject_ASSERT_WITH_MSG(op, gc_get_refs(op) > 0,
"refcount is too small");
gc_clear_unreachable(op);
GCState *gcstate = ((struct visit_heap_args *)args)->gcstate;
do {
traverseproc traverse = Py_TYPE(op)->tp_traverse;
(void) traverse(op,
(visitproc)visit_reachable_heap,
gcstate);
op = _PyObjectQueue_Pop(&gcstate->gc_work);
} while (op != NULL);
return true;
}
static bool
scan_heap_visitor(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* args)
{
VISITOR_BEGIN(block, args);
if (!_PyObject_GC_IS_TRACKED(op)) return true;
GCState *gcstate = ((struct visit_heap_args *)args)->gcstate;
gc_restore_tid(op);
if (!gc_is_unreachable(op)) {
// reachable
gcstate->long_lived_total++;
}
else if (has_legacy_finalizer(op)) {
// would be unreachable, but has legacy finalizer
gc_clear_unreachable(op);
gcstate->gc_uncollectable++;
if (gcstate->debug & DEBUG_UNCOLLECTABLE) {
debug_cycle("uncollectable", op);
}
/* 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.
*/
if (_PyList_AppendPrivate(gcstate->garbage, op) < 0) {
PyErr_Clear();
}
}
else {
// unreachable normal object
_PyObjectQueue_Push(&gcstate->gc_unreachable, op);
}
return true;
}
static void
reverse_queue(_PyObjectQueue **queue)
{
_PyObjectQueue *prev = NULL;
_PyObjectQueue *cur = *queue;
while (cur) {
_PyObjectQueue *next = cur->prev;
cur->prev = prev;
prev = cur;
cur = next;
}
*queue = prev;
}
static inline void
deduce_unreachable_heap(GCState *gcstate) {
struct visit_heap_args args = { .gcstate = gcstate };
visit_heaps(mark_heap_visitor, &args.base);
visit_heaps(scan_heap_visitor, &args.base);
// reverse the unreachable queue ordering to better match
// the order in which objects are allocated (not guaranteed!)
reverse_queue(&gcstate->gc_unreachable);
/* Clear weakrefs and enqueue callbacks. */
clear_weakrefs(gcstate);
}
/* A traversal callback for handle_resurrected_objects. */
static int
visit_decref_unreachable(PyObject *op, void *data)
{
if (PyObject_GC_IsTracked(op) && gc_is_unreachable(op)) {
// We are only interested in objects that are both tracked
// and in the unreachable queue. Note that some objects in the
// queue may have been untracked by finalizers.
gc_decref(op);
}
return 0;
}
/* Handle objects that may have resurrected after a call to 'finalize_garbage', moving
them to 'old_generation' and placing the rest on 'still_unreachable'.
Contracts:
* After this function 'unreachable' must not be used anymore and 'still_unreachable'
will contain the objects that did not resurrect.
* The "still_unreachable" list must be uninitialized (this function calls
gc_list_init over 'still_unreachable').
IMPORTANT: After a call to this function, the 'still_unreachable' set will have the
PREV_MARK_COLLECTING set, but the objects in this set are going to be removed so
we can skip the expense of clearing the flag to avoid extra iteration. */
static inline void
handle_resurrected_objects(GCState *gcstate)
{
_PyObjectQueue *q;
q = gcstate->gc_unreachable;
#ifdef Py_DEBUG
while (q != NULL) {
for (Py_ssize_t i = 0, n = q->n; i != n; i++) {
PyObject *op = q->objs[i];
assert(gc_get_refs(op) == 0);
assert(gc_is_unreachable(op));
assert(_Py_REF_IS_MERGED(op->ob_ref_shared));
}
q = q->prev;
}
#endif
// First reset the reference count for unreachable objects. Subtract one
// from the reference count to account for the refcount increment due
// to being in the "unreachable" list.
q = gcstate->gc_unreachable;
while (q != NULL) {
for (Py_ssize_t i = 0, n = q->n; i != n; i++) {