Coverage Report

Created: 2026-10-17 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
    // Don't flush: record_allocation() checks the threshold only when it fills.
225
    int young = _Py_atomic_load_int_relaxed(&gcstate->young.count);
226
    young += (int)gc->alloc_count;
227
    if (young < 0) {
228
        young = 0;
229
    }
230
#endif
231
232
0
#ifndef Py_GIL_DISABLED
233
0
    return Py_BuildValue("(iii)",
234
0
                         gcstate->generations[0].count,
235
0
                         gcstate->generations[1].count,
236
0
                         gcstate->generations[2].count);
237
#else
238
    return Py_BuildValue("(iii)",
239
                         young,
240
                         gcstate->old[0].count,
241
                         gcstate->old[1].count);
242
#endif
243
0
}
244
245
/*[clinic input]
246
gc.get_referrers
247
248
    *objs: tuple
249
250
Return the list of objects that directly refer to any of 'objs'.
251
[clinic start generated code]*/
252
253
static PyObject *
254
gc_get_referrers_impl(PyObject *module, PyObject *objs)
255
/*[clinic end generated code: output=929d6dff26f609b9 input=9102be7ebee69ee3]*/
256
0
{
257
0
    if (PySys_Audit("gc.get_referrers", "(O)", objs) < 0) {
258
0
        return NULL;
259
0
    }
260
261
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
262
0
    return _PyGC_GetReferrers(interp, objs);
263
0
}
264
265
/* Append obj to list; return true if error (out of memory), false if OK. */
266
static int
267
referentsvisit(PyObject *obj, void *arg)
268
0
{
269
0
    PyObject *list = arg;
270
0
    return PyList_Append(list, obj) < 0;
271
0
}
272
273
static int
274
append_referrents(PyObject *result, PyObject *args)
275
0
{
276
0
    for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(args); i++) {
277
0
        PyObject *obj = PyTuple_GET_ITEM(args, i);
278
0
        if (!_PyObject_IS_GC(obj)) {
279
0
            continue;
280
0
        }
281
282
0
        traverseproc traverse = Py_TYPE(obj)->tp_traverse;
283
0
        if (!traverse) {
284
0
            continue;
285
0
        }
286
0
        if (traverse(obj, referentsvisit, result)) {
287
0
            return -1;
288
0
        }
289
0
    }
290
0
    return 0;
291
0
}
292
293
/*[clinic input]
294
gc.get_referents
295
296
    *objs: tuple
297
298
Return the list of objects that are directly referred to by 'objs'.
299
[clinic start generated code]*/
300
301
static PyObject *
302
gc_get_referents_impl(PyObject *module, PyObject *objs)
303
/*[clinic end generated code: output=6dfde40cd1588e1d input=55c078a6d0248fe0]*/
304
0
{
305
0
    if (PySys_Audit("gc.get_referents", "(O)", objs) < 0) {
306
0
        return NULL;
307
0
    }
308
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
309
0
    PyObject *result = PyList_New(0);
310
311
0
    if (result == NULL) {
312
0
        return NULL;
313
0
    }
314
315
    // NOTE: stop the world is a no-op in default build
316
0
    _PyEval_StopTheWorld(interp);
317
0
    int err = append_referrents(result, objs);
318
0
    _PyEval_StartTheWorld(interp);
319
320
0
    if (err < 0) {
321
0
        Py_CLEAR(result);
322
0
    }
323
324
0
    return result;
325
0
}
326
327
/*[clinic input]
328
@permit_long_summary
329
gc.get_objects
330
    generation: Py_ssize_t(accept={int, NoneType}, c_default="-1") = None
331
        Generation to extract the objects from.
332
333
Return a list of objects tracked by the collector (excluding the list returned).
334
335
If generation is not None, return only the objects tracked by the
336
collector that are in that generation.
337
[clinic start generated code]*/
338
339
static PyObject *
340
gc_get_objects_impl(PyObject *module, Py_ssize_t generation)
341
/*[clinic end generated code: output=48b35fea4ba6cb0e input=89bca0d4a64e0135]*/
342
0
{
343
0
    if (PySys_Audit("gc.get_objects", "n", generation) < 0) {
344
0
        return NULL;
345
0
    }
346
347
0
    if (generation >= NUM_GENERATIONS) {
348
0
        return PyErr_Format(PyExc_ValueError,
349
0
                            "generation parameter must be less than the number of "
350
0
                            "available generations (%i)",
351
0
                            NUM_GENERATIONS);
352
0
    }
353
354
0
    if (generation < -1) {
355
0
        PyErr_SetString(PyExc_ValueError,
356
0
                        "generation parameter cannot be negative");
357
0
        return NULL;
358
0
    }
359
360
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
361
0
    return _PyGC_GetObjects(interp, (int)generation);
362
0
}
363
364
/*[clinic input]
365
gc.get_stats
366
367
Return a list of dictionaries containing per-generation statistics.
368
[clinic start generated code]*/
369
370
static PyObject *
371
gc_get_stats_impl(PyObject *module)
372
/*[clinic end generated code: output=a8ab1d8a5d26f3ab input=1ef4ed9d17b1a470]*/
373
0
{
374
0
    int i;
375
0
    struct gc_generation_stats stats[NUM_GENERATIONS], *st;
376
377
    /* To get consistent values despite allocations while constructing
378
       the result list, we use a snapshot of the running stats. */
379
0
    GCState *gcstate = get_gc_state();
380
#ifdef Py_GIL_DISABLED
381
    PyMutex_Lock(&gcstate->stats_mutex);
382
#endif
383
0
    stats[0] = gcstate->generation_stats->young.items[gcstate->generation_stats->young.index];
384
0
    stats[1] = gcstate->generation_stats->old[0].items[gcstate->generation_stats->old[0].index];
385
0
    stats[2] = gcstate->generation_stats->old[1].items[gcstate->generation_stats->old[1].index];
386
#ifdef Py_GIL_DISABLED
387
    PyMutex_Unlock(&gcstate->stats_mutex);
388
#endif
389
390
0
    PyObject *result = PyList_New(0);
391
0
    if (result == NULL)
392
0
        return NULL;
393
394
0
    for (i = 0; i < NUM_GENERATIONS; i++) {
395
0
        PyObject *dict;
396
0
        st = &stats[i];
397
0
        dict = Py_BuildValue("{snsnsnsnsd}",
398
0
                             "collections", st->collections,
399
0
                             "collected", st->collected,
400
0
                             "uncollectable", st->uncollectable,
401
0
                             "candidates", st->candidates,
402
0
                             "duration", st->duration
403
0
                            );
404
0
        if (dict == NULL)
405
0
            goto error;
406
0
        if (PyList_Append(result, dict)) {
407
0
            Py_DECREF(dict);
408
0
            goto error;
409
0
        }
410
0
        Py_DECREF(dict);
411
0
    }
412
0
    return result;
413
414
0
error:
415
0
    Py_XDECREF(result);
416
0
    return NULL;
417
0
}
418
419
420
/*[clinic input]
421
gc.is_tracked -> bool
422
423
    obj: object
424
    /
425
426
Returns true if the object is tracked by the garbage collector.
427
428
Simple atomic objects will return false.
429
[clinic start generated code]*/
430
431
static int
432
gc_is_tracked_impl(PyObject *module, PyObject *obj)
433
/*[clinic end generated code: output=91c8d086b7f47a33 input=423b98ec680c3126]*/
434
0
{
435
0
    return PyObject_GC_IsTracked(obj);
436
0
}
437
438
/*[clinic input]
439
gc.is_finalized -> bool
440
441
    obj: object
442
    /
443
444
Returns true if the object has been already finalized by the GC.
445
[clinic start generated code]*/
446
447
static int
448
gc_is_finalized_impl(PyObject *module, PyObject *obj)
449
/*[clinic end generated code: output=401ff5d6fc660429 input=ca4d111c8f8c4e3a]*/
450
0
{
451
0
    return PyObject_GC_IsFinalized(obj);
452
0
}
453
454
/*[clinic input]
455
@permit_long_summary
456
gc.freeze
457
458
Freeze all current tracked objects and ignore them for future collections.
459
460
This can be used before a POSIX fork() call to make the gc copy-on-write
461
friendly.
462
Note: collection before a POSIX fork() call may free pages for future
463
allocation which can cause copy-on-write.
464
[clinic start generated code]*/
465
466
static PyObject *
467
gc_freeze_impl(PyObject *module)
468
/*[clinic end generated code: output=502159d9cdc4c139 input=02674706fc9c0de6]*/
469
0
{
470
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
471
0
    _PyGC_Freeze(interp);
472
0
    Py_RETURN_NONE;
473
0
}
474
475
/*[clinic input]
476
gc.unfreeze
477
478
Unfreeze all objects in the permanent generation.
479
480
Put all objects in the permanent generation back into oldest generation.
481
[clinic start generated code]*/
482
483
static PyObject *
484
gc_unfreeze_impl(PyObject *module)
485
/*[clinic end generated code: output=1c15f2043b25e169 input=2dd52b170f4cef6c]*/
486
0
{
487
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
488
0
    _PyGC_Unfreeze(interp);
489
0
    Py_RETURN_NONE;
490
0
}
491
492
/*[clinic input]
493
gc.get_freeze_count -> Py_ssize_t
494
495
Return the number of objects in the permanent generation.
496
[clinic start generated code]*/
497
498
static Py_ssize_t
499
gc_get_freeze_count_impl(PyObject *module)
500
/*[clinic end generated code: output=61cbd9f43aa032e1 input=45ffbc65cfe2a6ed]*/
501
0
{
502
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
503
0
    return _PyGC_GetFreezeCount(interp);
504
0
}
505
506
507
PyDoc_STRVAR(gc__doc__,
508
"This module provides access to the garbage collector for reference cycles.\n"
509
"\n"
510
"enable() -- Enable automatic garbage collection.\n"
511
"disable() -- Disable automatic garbage collection.\n"
512
"isenabled() -- Returns true if automatic collection is enabled.\n"
513
"collect() -- Do a full collection right now.\n"
514
"get_count() -- Return the current collection counts.\n"
515
"get_stats() -- Return list of dictionaries containing per-generation stats.\n"
516
"set_debug() -- Set debugging flags.\n"
517
"get_debug() -- Get debugging flags.\n"
518
"set_threshold() -- Set the collection thresholds.\n"
519
"get_threshold() -- Return the current collection thresholds.\n"
520
"get_objects() -- Return a list of all objects tracked by the collector.\n"
521
"is_tracked() -- Returns true if a given object is tracked.\n"
522
"is_finalized() -- Returns true if a given object has been already finalized.\n"
523
"get_referrers() -- Return the list of objects that refer to an object.\n"
524
"get_referents() -- Return the list of objects that an object refers to.\n"
525
"freeze() -- Freeze all tracked objects and ignore them for future collections.\n"
526
"unfreeze() -- Unfreeze all objects in the permanent generation.\n"
527
"get_freeze_count() -- Return the number of objects in the permanent generation.\n");
528
529
static PyMethodDef GcMethods[] = {
530
    GC_ENABLE_METHODDEF
531
    GC_DISABLE_METHODDEF
532
    GC_ISENABLED_METHODDEF
533
    GC_SET_DEBUG_METHODDEF
534
    GC_GET_DEBUG_METHODDEF
535
    GC_GET_COUNT_METHODDEF
536
    GC_SET_THRESHOLD_METHODDEF
537
    GC_GET_THRESHOLD_METHODDEF
538
    GC_COLLECT_METHODDEF
539
    GC_GET_OBJECTS_METHODDEF
540
    GC_GET_STATS_METHODDEF
541
    GC_IS_TRACKED_METHODDEF
542
    GC_IS_FINALIZED_METHODDEF
543
    GC_GET_REFERRERS_METHODDEF
544
    GC_GET_REFERENTS_METHODDEF
545
    GC_FREEZE_METHODDEF
546
    GC_UNFREEZE_METHODDEF
547
    GC_GET_FREEZE_COUNT_METHODDEF
548
    {NULL,      NULL}           /* Sentinel */
549
};
550
551
static int
552
gcmodule_exec(PyObject *module)
553
4
{
554
4
    GCState *gcstate = get_gc_state();
555
556
    /* garbage and callbacks are initialized by _PyGC_Init() early in
557
     * interpreter lifecycle. */
558
4
    assert(gcstate->garbage != NULL);
559
4
    if (PyModule_AddObjectRef(module, "garbage", gcstate->garbage) < 0) {
560
0
        return -1;
561
0
    }
562
4
    assert(gcstate->callbacks != NULL);
563
4
    if (PyModule_AddObjectRef(module, "callbacks", gcstate->callbacks) < 0) {
564
0
        return -1;
565
0
    }
566
567
20
#define ADD_INT(NAME) if (PyModule_AddIntConstant(module, #NAME, _PyGC_ ## NAME) < 0) { return -1; }
568
4
    ADD_INT(DEBUG_STATS);
569
4
    ADD_INT(DEBUG_COLLECTABLE);
570
4
    ADD_INT(DEBUG_UNCOLLECTABLE);
571
4
    ADD_INT(DEBUG_SAVEALL);
572
4
    ADD_INT(DEBUG_LEAK);
573
4
#undef ADD_INT
574
4
    return 0;
575
4
}
576
577
static PyModuleDef_Slot gcmodule_slots[] = {
578
    _Py_ABI_SLOT,
579
    {Py_mod_exec, gcmodule_exec},
580
    {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
581
    {Py_mod_gil, Py_MOD_GIL_NOT_USED},
582
    {0, NULL}
583
};
584
585
static struct PyModuleDef gcmodule = {
586
    PyModuleDef_HEAD_INIT,
587
    .m_name = "gc",
588
    .m_doc = gc__doc__,
589
    .m_size = 0,  // per interpreter state, see: get_gc_state()
590
    .m_methods = GcMethods,
591
    .m_slots = gcmodule_slots
592
};
593
594
PyMODINIT_FUNC
595
PyInit_gc(void)
596
4
{
597
4
    return PyModuleDef_Init(&gcmodule);
598
4
}