Coverage Report

Created: 2026-06-21 06:15

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