Coverage Report

Created: 2026-10-31 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Modules/gcmodule.c
Line
Count
Source
1
/*
2
 * Python interface to the garbage collector.
3
 *
4
 * See Python/gc.c for the implementation of the garbage collector.
5
 */
6
7
#include "Python.h"
8
#include "pycore_gc.h"
9
#include "pycore_object.h"      // _PyObject_IS_GC()
10
#include "pycore_pyatomic_ft_wrappers.h"  // FT_ATOMIC_LOAD_INT_RELAXED()
11
#include "pycore_pystate.h"     // _PyInterpreterState_GET()
12
13
typedef struct _gc_runtime_state GCState;
14
15
static GCState *
16
get_gc_state(void)
17
4
{
18
4
    PyInterpreterState *interp = _PyInterpreterState_GET();
19
4
    return &interp->gc;
20
4
}
21
22
/*[clinic input]
23
module gc
24
[clinic start generated code]*/
25
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=b5c9690ecc842d79]*/
26
#include "clinic/gcmodule.c.h"
27
28
/*[clinic input]
29
gc.enable
30
31
Enable automatic garbage collection.
32
[clinic start generated code]*/
33
34
static PyObject *
35
gc_enable_impl(PyObject *module)
36
/*[clinic end generated code: output=45a427e9dce9155c input=81ac4940ca579707]*/
37
0
{
38
0
    PyGC_Enable();
39
0
    Py_RETURN_NONE;
40
0
}
41
42
/*[clinic input]
43
gc.disable
44
45
Disable automatic garbage collection.
46
[clinic start generated code]*/
47
48
static PyObject *
49
gc_disable_impl(PyObject *module)
50
/*[clinic end generated code: output=97d1030f7aa9d279 input=8c2e5a14e800d83b]*/
51
0
{
52
0
    PyGC_Disable();
53
0
    Py_RETURN_NONE;
54
0
}
55
56
/*[clinic input]
57
gc.isenabled -> bool
58
59
Returns true if automatic garbage collection is enabled.
60
[clinic start generated code]*/
61
62
static int
63
gc_isenabled_impl(PyObject *module)
64
/*[clinic end generated code: output=1874298331c49130 input=30005e0422373b31]*/
65
0
{
66
0
    return PyGC_IsEnabled();
67
0
}
68
69
/*[clinic input]
70
gc.collect -> Py_ssize_t
71
72
    generation: int(c_default="NUM_GENERATIONS - 1") = 2
73
74
Run the garbage collector.
75
76
With no arguments, run a full collection.  The optional argument
77
may be an integer specifying which generation to collect.  A ValueError
78
is raised if the generation number is invalid.
79
80
The number of unreachable objects is returned.
81
[clinic start generated code]*/
82
83
static Py_ssize_t
84
gc_collect_impl(PyObject *module, int generation)
85
/*[clinic end generated code: output=b697e633043233c7 input=40720128b682d879]*/
86
0
{
87
0
    PyThreadState *tstate = _PyThreadState_GET();
88
89
0
    if (generation < 0 || generation >= NUM_GENERATIONS) {
90
0
        _PyErr_SetString(tstate, PyExc_ValueError, "invalid generation");
91
0
        return -1;
92
0
    }
93
94
0
    return _PyGC_Collect(tstate, generation, _Py_GC_REASON_MANUAL);
95
0
}
96
97
/*[clinic input]
98
gc.set_debug
99
100
    flags: int
101
        An integer that can have the following bits turned on:
102
          DEBUG_STATS - Print statistics during collection.
103
          DEBUG_COLLECTABLE - Print collectable objects found.
104
          DEBUG_UNCOLLECTABLE - Print unreachable but uncollectable objects
105
            found.
106
          DEBUG_SAVEALL - Save objects to gc.garbage rather than freeing them.
107
          DEBUG_LEAK - Debug leaking programs (everything but STATS).
108
    /
109
110
Set the garbage collection debugging flags.
111
112
Debugging information is written to sys.stderr.
113
[clinic start generated code]*/
114
115
static PyObject *
116
gc_set_debug_impl(PyObject *module, int flags)
117
/*[clinic end generated code: output=7c8366575486b228 input=5e5ce15e84fbed15]*/
118
0
{
119
0
    GCState *gcstate = get_gc_state();
120
0
    FT_ATOMIC_STORE_INT_RELAXED(gcstate->debug, flags);
121
0
    Py_RETURN_NONE;
122
0
}
123
124
/*[clinic input]
125
gc.get_debug -> int
126
127
Get the garbage collection debugging flags.
128
[clinic start generated code]*/
129
130
static int
131
gc_get_debug_impl(PyObject *module)
132
/*[clinic end generated code: output=91242f3506cd1e50 input=91a101e1c3b98366]*/
133
0
{
134
0
    GCState *gcstate = get_gc_state();
135
0
    return FT_ATOMIC_LOAD_INT_RELAXED(gcstate->debug);
136
0
}
137
138
/*[clinic input]
139
gc.set_threshold
140
141
    threshold0: int
142
    [
143
    threshold1: int
144
    [
145
    threshold2: int
146
    ]
147
    ]
148
    /
149
150
Set the collection thresholds (the collection frequency).
151
152
Setting 'threshold0' to zero disables collection.
153
[clinic start generated code]*/
154
155
static PyObject *
156
gc_set_threshold_impl(PyObject *module, int threshold0, int group_right_1,
157
                      int threshold1, int group_right_2, int threshold2)
158
/*[clinic end generated code: output=2e3c7c7dd59060f3 input=0d9612db50984eec]*/
159
0
{
160
0
    GCState *gcstate = get_gc_state();
161
162
0
#ifndef Py_GIL_DISABLED
163
0
    gcstate->generations[0].threshold = threshold0;
164
0
    if (group_right_1) {
165
0
        gcstate->generations[1].threshold = threshold1;
166
0
    }
167
0
    if (group_right_2) {
168
0
        gcstate->generations[2].threshold = threshold2;
169
0
    }
170
#else
171
    PyInterpreterState *interp = _PyInterpreterState_GET();
172
    _PyEval_StopTheWorld(interp);
173
    gcstate->young.threshold = threshold0;
174
    if (group_right_1) {
175
        gcstate->old[0].threshold = threshold1;
176
    }
177
    if (group_right_2) {
178
        gcstate->old[1].threshold = threshold2;
179
    }
180
    _PyEval_StartTheWorld(interp);
181
#endif
182
0
    Py_RETURN_NONE;
183
0
}
184
185
/*[clinic input]
186
gc.get_threshold
187
188
Return the current collection thresholds.
189
[clinic start generated code]*/
190
191
static PyObject *
192
gc_get_threshold_impl(PyObject *module)
193
/*[clinic end generated code: output=7902bc9f41ecbbd8 input=286d79918034d6e6]*/
194
0
{
195
0
    GCState *gcstate = get_gc_state();
196
0
#ifndef Py_GIL_DISABLED
197
0
    return Py_BuildValue("(iii)",
198
0
                         gcstate->generations[0].threshold,
199
0
                         gcstate->generations[1].threshold,
200
0
                         gcstate->generations[2].threshold);
201
#else
202
    return Py_BuildValue("(iii)",
203
                         gcstate->young.threshold,
204
                         gcstate->old[0].threshold,
205
                         gcstate->old[1].threshold);
206
#endif
207
0
}
208
209
/*[clinic input]
210
gc.get_count
211
212
Return a three-tuple of the current collection counts.
213
[clinic start generated code]*/
214
215
static PyObject *
216
gc_get_count_impl(PyObject *module)
217
/*[clinic end generated code: output=354012e67b16398f input=a392794a08251751]*/
218
0
{
219
0
    GCState *gcstate = get_gc_state();
220
221
#ifdef Py_GIL_DISABLED
222
    _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET();
223
    struct _gc_thread_state *gc = &tstate->gc;
224
225
    // Don't flush: record_allocation() checks the threshold only when it fills.
226
    int young = _Py_atomic_load_int_relaxed(&gcstate->young.count);
227
    young += (int)gc->alloc_count;
228
    if (young < 0) {
229
        young = 0;
230
    }
231
#endif
232
233
0
#ifndef Py_GIL_DISABLED
234
0
    return Py_BuildValue("(iii)",
235
0
                         gcstate->generations[0].count,
236
0
                         gcstate->generations[1].count,
237
0
                         gcstate->generations[2].count);
238
#else
239
    return Py_BuildValue("(iii)",
240
                         young,
241
                         gcstate->old[0].count,
242
                         gcstate->old[1].count);
243
#endif
244
0
}
245
246
/*[clinic input]
247
gc.get_referrers
248
249
    *objs: tuple
250
251
Return the list of objects that directly refer to any of 'objs'.
252
[clinic start generated code]*/
253
254
static PyObject *
255
gc_get_referrers_impl(PyObject *module, PyObject *objs)
256
/*[clinic end generated code: output=929d6dff26f609b9 input=9102be7ebee69ee3]*/
257
0
{
258
0
    if (PySys_Audit("gc.get_referrers", "(O)", objs) < 0) {
259
0
        return NULL;
260
0
    }
261
262
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
263
0
    return _PyGC_GetReferrers(interp, objs);
264
0
}
265
266
/* Append obj to list; return true if error (out of memory), false if OK. */
267
static int
268
referentsvisit(PyObject *obj, void *arg)
269
0
{
270
0
    PyObject *list = arg;
271
0
    return PyList_Append(list, obj) < 0;
272
0
}
273
274
static int
275
append_referrents(PyObject *result, PyObject *args)
276
0
{
277
0
    for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(args); i++) {
278
0
        PyObject *obj = PyTuple_GET_ITEM(args, i);
279
0
        if (!_PyObject_IS_GC(obj)) {
280
0
            continue;
281
0
        }
282
283
0
        traverseproc traverse = Py_TYPE(obj)->tp_traverse;
284
0
        if (!traverse) {
285
0
            continue;
286
0
        }
287
0
        if (traverse(obj, referentsvisit, result)) {
288
0
            return -1;
289
0
        }
290
0
    }
291
0
    return 0;
292
0
}
293
294
/*[clinic input]
295
gc.get_referents
296
297
    *objs: tuple
298
299
Return the list of objects that are directly referred to by 'objs'.
300
[clinic start generated code]*/
301
302
static PyObject *
303
gc_get_referents_impl(PyObject *module, PyObject *objs)
304
/*[clinic end generated code: output=6dfde40cd1588e1d input=55c078a6d0248fe0]*/
305
0
{
306
0
    if (PySys_Audit("gc.get_referents", "(O)", objs) < 0) {
307
0
        return NULL;
308
0
    }
309
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
310
0
    PyObject *result = PyList_New(0);
311
312
0
    if (result == NULL) {
313
0
        return NULL;
314
0
    }
315
316
    // NOTE: stop the world is a no-op in default build
317
0
    _PyEval_StopTheWorld(interp);
318
0
    int err = append_referrents(result, objs);
319
0
    _PyEval_StartTheWorld(interp);
320
321
0
    if (err < 0) {
322
0
        Py_CLEAR(result);
323
0
    }
324
325
0
    return result;
326
0
}
327
328
/*[clinic input]
329
@permit_long_summary
330
gc.get_objects
331
    generation: Py_ssize_t(accept={int, NoneType}, c_default="-1") = None
332
        Generation to extract the objects from.
333
334
Return a list of objects tracked by the collector (excluding the list returned).
335
336
If generation is not None, return only the objects tracked by the
337
collector that are in that generation.
338
[clinic start generated code]*/
339
340
static PyObject *
341
gc_get_objects_impl(PyObject *module, Py_ssize_t generation)
342
/*[clinic end generated code: output=48b35fea4ba6cb0e input=89bca0d4a64e0135]*/
343
0
{
344
0
    if (PySys_Audit("gc.get_objects", "n", generation) < 0) {
345
0
        return NULL;
346
0
    }
347
348
0
    if (generation >= NUM_GENERATIONS) {
349
0
        return PyErr_Format(PyExc_ValueError,
350
0
                            "generation parameter must be less than the number of "
351
0
                            "available generations (%i)",
352
0
                            NUM_GENERATIONS);
353
0
    }
354
355
0
    if (generation < -1) {
356
0
        PyErr_SetString(PyExc_ValueError,
357
0
                        "generation parameter cannot be negative");
358
0
        return NULL;
359
0
    }
360
361
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
362
0
    return _PyGC_GetObjects(interp, (int)generation);
363
0
}
364
365
/*[clinic input]
366
gc.get_stats
367
368
Return a list of dictionaries containing per-generation statistics.
369
[clinic start generated code]*/
370
371
static PyObject *
372
gc_get_stats_impl(PyObject *module)
373
/*[clinic end generated code: output=a8ab1d8a5d26f3ab input=1ef4ed9d17b1a470]*/
374
0
{
375
0
    int i;
376
0
    struct gc_generation_stats stats[NUM_GENERATIONS], *st;
377
378
    /* To get consistent values despite allocations while constructing
379
       the result list, we use a snapshot of the running stats. */
380
0
    GCState *gcstate = get_gc_state();
381
#ifdef Py_GIL_DISABLED
382
    PyMutex_Lock(&gcstate->stats_mutex);
383
#endif
384
0
    stats[0] = gcstate->generation_stats->young.items[gcstate->generation_stats->young.index];
385
0
    stats[1] = gcstate->generation_stats->old[0].items[gcstate->generation_stats->old[0].index];
386
0
    stats[2] = gcstate->generation_stats->old[1].items[gcstate->generation_stats->old[1].index];
387
#ifdef Py_GIL_DISABLED
388
    PyMutex_Unlock(&gcstate->stats_mutex);
389
#endif
390
391
0
    PyObject *result = PyList_New(0);
392
0
    if (result == NULL)
393
0
        return NULL;
394
395
0
    for (i = 0; i < NUM_GENERATIONS; i++) {
396
0
        PyObject *dict;
397
0
        st = &stats[i];
398
0
        dict = Py_BuildValue("{snsnsnsnsd}",
399
0
                             "collections", st->collections,
400
0
                             "collected", st->collected,
401
0
                             "uncollectable", st->uncollectable,
402
0
                             "candidates", st->candidates,
403
0
                             "duration", st->duration
404
0
                            );
405
0
        if (dict == NULL)
406
0
            goto error;
407
0
        if (PyList_Append(result, dict)) {
408
0
            Py_DECREF(dict);
409
0
            goto error;
410
0
        }
411
0
        Py_DECREF(dict);
412
0
    }
413
0
    return result;
414
415
0
error:
416
0
    Py_XDECREF(result);
417
0
    return NULL;
418
0
}
419
420
421
/*[clinic input]
422
gc.is_tracked -> bool
423
424
    obj: object
425
    /
426
427
Returns true if the object is tracked by the garbage collector.
428
429
Simple atomic objects will return false.
430
[clinic start generated code]*/
431
432
static int
433
gc_is_tracked_impl(PyObject *module, PyObject *obj)
434
/*[clinic end generated code: output=91c8d086b7f47a33 input=423b98ec680c3126]*/
435
0
{
436
0
    return PyObject_GC_IsTracked(obj);
437
0
}
438
439
/*[clinic input]
440
gc.is_finalized -> bool
441
442
    obj: object
443
    /
444
445
Returns true if the object has been already finalized by the GC.
446
[clinic start generated code]*/
447
448
static int
449
gc_is_finalized_impl(PyObject *module, PyObject *obj)
450
/*[clinic end generated code: output=401ff5d6fc660429 input=ca4d111c8f8c4e3a]*/
451
0
{
452
0
    return PyObject_GC_IsFinalized(obj);
453
0
}
454
455
/*[clinic input]
456
@permit_long_summary
457
gc.freeze
458
459
Freeze all current tracked objects and ignore them for future collections.
460
461
This can be used before a POSIX fork() call to make the gc copy-on-write
462
friendly.
463
Note: collection before a POSIX fork() call may free pages for future
464
allocation which can cause copy-on-write.
465
[clinic start generated code]*/
466
467
static PyObject *
468
gc_freeze_impl(PyObject *module)
469
/*[clinic end generated code: output=502159d9cdc4c139 input=02674706fc9c0de6]*/
470
0
{
471
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
472
0
    _PyGC_Freeze(interp);
473
0
    Py_RETURN_NONE;
474
0
}
475
476
/*[clinic input]
477
gc.unfreeze
478
479
Unfreeze all objects in the permanent generation.
480
481
Put all objects in the permanent generation back into oldest generation.
482
[clinic start generated code]*/
483
484
static PyObject *
485
gc_unfreeze_impl(PyObject *module)
486
/*[clinic end generated code: output=1c15f2043b25e169 input=2dd52b170f4cef6c]*/
487
0
{
488
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
489
0
    _PyGC_Unfreeze(interp);
490
0
    Py_RETURN_NONE;
491
0
}
492
493
/*[clinic input]
494
gc.get_freeze_count -> Py_ssize_t
495
496
Return the number of objects in the permanent generation.
497
[clinic start generated code]*/
498
499
static Py_ssize_t
500
gc_get_freeze_count_impl(PyObject *module)
501
/*[clinic end generated code: output=61cbd9f43aa032e1 input=45ffbc65cfe2a6ed]*/
502
0
{
503
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
504
0
    return _PyGC_GetFreezeCount(interp);
505
0
}
506
507
508
PyDoc_STRVAR(gc__doc__,
509
"This module provides access to the garbage collector for reference cycles.\n"
510
"\n"
511
"enable() -- Enable automatic garbage collection.\n"
512
"disable() -- Disable automatic garbage collection.\n"
513
"isenabled() -- Returns true if automatic collection is enabled.\n"
514
"collect() -- Do a full collection right now.\n"
515
"get_count() -- Return the current collection counts.\n"
516
"get_stats() -- Return list of dictionaries containing per-generation stats.\n"
517
"set_debug() -- Set debugging flags.\n"
518
"get_debug() -- Get debugging flags.\n"
519
"set_threshold() -- Set the collection thresholds.\n"
520
"get_threshold() -- Return the current collection thresholds.\n"
521
"get_objects() -- Return a list of all objects tracked by the collector.\n"
522
"is_tracked() -- Returns true if a given object is tracked.\n"
523
"is_finalized() -- Returns true if a given object has been already finalized.\n"
524
"get_referrers() -- Return the list of objects that refer to an object.\n"
525
"get_referents() -- Return the list of objects that an object refers to.\n"
526
"freeze() -- Freeze all tracked objects and ignore them for future collections.\n"
527
"unfreeze() -- Unfreeze all objects in the permanent generation.\n"
528
"get_freeze_count() -- Return the number of objects in the permanent generation.\n");
529
530
static PyMethodDef GcMethods[] = {
531
    GC_ENABLE_METHODDEF
532
    GC_DISABLE_METHODDEF
533
    GC_ISENABLED_METHODDEF
534
    GC_SET_DEBUG_METHODDEF
535
    GC_GET_DEBUG_METHODDEF
536
    GC_GET_COUNT_METHODDEF
537
    GC_SET_THRESHOLD_METHODDEF
538
    GC_GET_THRESHOLD_METHODDEF
539
    GC_COLLECT_METHODDEF
540
    GC_GET_OBJECTS_METHODDEF
541
    GC_GET_STATS_METHODDEF
542
    GC_IS_TRACKED_METHODDEF
543
    GC_IS_FINALIZED_METHODDEF
544
    GC_GET_REFERRERS_METHODDEF
545
    GC_GET_REFERENTS_METHODDEF
546
    GC_FREEZE_METHODDEF
547
    GC_UNFREEZE_METHODDEF
548
    GC_GET_FREEZE_COUNT_METHODDEF
549
    {NULL,      NULL}           /* Sentinel */
550
};
551
552
static int
553
gcmodule_exec(PyObject *module)
554
4
{
555
4
    GCState *gcstate = get_gc_state();
556
557
    /* garbage and callbacks are initialized by _PyGC_Init() early in
558
     * interpreter lifecycle. */
559
4
    assert(gcstate->garbage != NULL);
560
4
    if (PyModule_AddObjectRef(module, "garbage", gcstate->garbage) < 0) {
561
0
        return -1;
562
0
    }
563
4
    assert(gcstate->callbacks != NULL);
564
4
    if (PyModule_AddObjectRef(module, "callbacks", gcstate->callbacks) < 0) {
565
0
        return -1;
566
0
    }
567
568
20
#define ADD_INT(NAME) if (PyModule_AddIntConstant(module, #NAME, _PyGC_ ## NAME) < 0) { return -1; }
569
4
    ADD_INT(DEBUG_STATS);
570
4
    ADD_INT(DEBUG_COLLECTABLE);
571
4
    ADD_INT(DEBUG_UNCOLLECTABLE);
572
4
    ADD_INT(DEBUG_SAVEALL);
573
4
    ADD_INT(DEBUG_LEAK);
574
4
#undef ADD_INT
575
4
    return 0;
576
4
}
577
578
static PyModuleDef_Slot gcmodule_slots[] = {
579
    _Py_ABI_SLOT,
580
    {Py_mod_exec, gcmodule_exec},
581
    {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
582
    {Py_mod_gil, Py_MOD_GIL_NOT_USED},
583
    {0, NULL}
584
};
585
586
static struct PyModuleDef gcmodule = {
587
    PyModuleDef_HEAD_INIT,
588
    .m_name = "gc",
589
    .m_doc = gc__doc__,
590
    .m_size = 0,  // per interpreter state, see: get_gc_state()
591
    .m_methods = GcMethods,
592
    .m_slots = gcmodule_slots
593
};
594
595
PyMODINIT_FUNC
596
PyInit_gc(void)
597
4
{
598
4
    return PyModuleDef_Init(&gcmodule);
599
4
}