Coverage Report

Created: 2026-07-14 06:16

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
#ifdef Py_GIL_DISABLED
378
    PyMutex_Lock(&gcstate->stats_mutex);
379
#endif
380
0
    stats[0] = gcstate->generation_stats->young.items[gcstate->generation_stats->young.index];
381
0
    stats[1] = gcstate->generation_stats->old[0].items[gcstate->generation_stats->old[0].index];
382
0
    stats[2] = gcstate->generation_stats->old[1].items[gcstate->generation_stats->old[1].index];
383
#ifdef Py_GIL_DISABLED
384
    PyMutex_Unlock(&gcstate->stats_mutex);
385
#endif
386
387
0
    PyObject *result = PyList_New(0);
388
0
    if (result == NULL)
389
0
        return NULL;
390
391
0
    for (i = 0; i < NUM_GENERATIONS; i++) {
392
0
        PyObject *dict;
393
0
        st = &stats[i];
394
0
        dict = Py_BuildValue("{snsnsnsnsd}",
395
0
                             "collections", st->collections,
396
0
                             "collected", st->collected,
397
0
                             "uncollectable", st->uncollectable,
398
0
                             "candidates", st->candidates,
399
0
                             "duration", st->duration
400
0
                            );
401
0
        if (dict == NULL)
402
0
            goto error;
403
0
        if (PyList_Append(result, dict)) {
404
0
            Py_DECREF(dict);
405
0
            goto error;
406
0
        }
407
0
        Py_DECREF(dict);
408
0
    }
409
0
    return result;
410
411
0
error:
412
0
    Py_XDECREF(result);
413
0
    return NULL;
414
0
}
415
416
417
/*[clinic input]
418
gc.is_tracked -> bool
419
420
    obj: object
421
    /
422
423
Returns true if the object is tracked by the garbage collector.
424
425
Simple atomic objects will return false.
426
[clinic start generated code]*/
427
428
static int
429
gc_is_tracked_impl(PyObject *module, PyObject *obj)
430
/*[clinic end generated code: output=91c8d086b7f47a33 input=423b98ec680c3126]*/
431
0
{
432
0
    return PyObject_GC_IsTracked(obj);
433
0
}
434
435
/*[clinic input]
436
gc.is_finalized -> bool
437
438
    obj: object
439
    /
440
441
Returns true if the object has been already finalized by the GC.
442
[clinic start generated code]*/
443
444
static int
445
gc_is_finalized_impl(PyObject *module, PyObject *obj)
446
/*[clinic end generated code: output=401ff5d6fc660429 input=ca4d111c8f8c4e3a]*/
447
0
{
448
0
    return PyObject_GC_IsFinalized(obj);
449
0
}
450
451
/*[clinic input]
452
@permit_long_summary
453
gc.freeze
454
455
Freeze all current tracked objects and ignore them for future collections.
456
457
This can be used before a POSIX fork() call to make the gc copy-on-write
458
friendly.
459
Note: collection before a POSIX fork() call may free pages for future
460
allocation which can cause copy-on-write.
461
[clinic start generated code]*/
462
463
static PyObject *
464
gc_freeze_impl(PyObject *module)
465
/*[clinic end generated code: output=502159d9cdc4c139 input=02674706fc9c0de6]*/
466
0
{
467
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
468
0
    _PyGC_Freeze(interp);
469
0
    Py_RETURN_NONE;
470
0
}
471
472
/*[clinic input]
473
gc.unfreeze
474
475
Unfreeze all objects in the permanent generation.
476
477
Put all objects in the permanent generation back into oldest generation.
478
[clinic start generated code]*/
479
480
static PyObject *
481
gc_unfreeze_impl(PyObject *module)
482
/*[clinic end generated code: output=1c15f2043b25e169 input=2dd52b170f4cef6c]*/
483
0
{
484
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
485
0
    _PyGC_Unfreeze(interp);
486
0
    Py_RETURN_NONE;
487
0
}
488
489
/*[clinic input]
490
gc.get_freeze_count -> Py_ssize_t
491
492
Return the number of objects in the permanent generation.
493
[clinic start generated code]*/
494
495
static Py_ssize_t
496
gc_get_freeze_count_impl(PyObject *module)
497
/*[clinic end generated code: output=61cbd9f43aa032e1 input=45ffbc65cfe2a6ed]*/
498
0
{
499
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
500
0
    return _PyGC_GetFreezeCount(interp);
501
0
}
502
503
504
PyDoc_STRVAR(gc__doc__,
505
"This module provides access to the garbage collector for reference cycles.\n"
506
"\n"
507
"enable() -- Enable automatic garbage collection.\n"
508
"disable() -- Disable automatic garbage collection.\n"
509
"isenabled() -- Returns true if automatic collection is enabled.\n"
510
"collect() -- Do a full collection right now.\n"
511
"get_count() -- Return the current collection counts.\n"
512
"get_stats() -- Return list of dictionaries containing per-generation stats.\n"
513
"set_debug() -- Set debugging flags.\n"
514
"get_debug() -- Get debugging flags.\n"
515
"set_threshold() -- Set the collection thresholds.\n"
516
"get_threshold() -- Return the current collection thresholds.\n"
517
"get_objects() -- Return a list of all objects tracked by the collector.\n"
518
"is_tracked() -- Returns true if a given object is tracked.\n"
519
"is_finalized() -- Returns true if a given object has been already finalized.\n"
520
"get_referrers() -- Return the list of objects that refer to an object.\n"
521
"get_referents() -- Return the list of objects that an object refers to.\n"
522
"freeze() -- Freeze all tracked objects and ignore them for future collections.\n"
523
"unfreeze() -- Unfreeze all objects in the permanent generation.\n"
524
"get_freeze_count() -- Return the number of objects in the permanent generation.\n");
525
526
static PyMethodDef GcMethods[] = {
527
    GC_ENABLE_METHODDEF
528
    GC_DISABLE_METHODDEF
529
    GC_ISENABLED_METHODDEF
530
    GC_SET_DEBUG_METHODDEF
531
    GC_GET_DEBUG_METHODDEF
532
    GC_GET_COUNT_METHODDEF
533
    GC_SET_THRESHOLD_METHODDEF
534
    GC_GET_THRESHOLD_METHODDEF
535
    GC_COLLECT_METHODDEF
536
    GC_GET_OBJECTS_METHODDEF
537
    GC_GET_STATS_METHODDEF
538
    GC_IS_TRACKED_METHODDEF
539
    GC_IS_FINALIZED_METHODDEF
540
    GC_GET_REFERRERS_METHODDEF
541
    GC_GET_REFERENTS_METHODDEF
542
    GC_FREEZE_METHODDEF
543
    GC_UNFREEZE_METHODDEF
544
    GC_GET_FREEZE_COUNT_METHODDEF
545
    {NULL,      NULL}           /* Sentinel */
546
};
547
548
static int
549
gcmodule_exec(PyObject *module)
550
4
{
551
4
    GCState *gcstate = get_gc_state();
552
553
    /* garbage and callbacks are initialized by _PyGC_Init() early in
554
     * interpreter lifecycle. */
555
4
    assert(gcstate->garbage != NULL);
556
4
    if (PyModule_AddObjectRef(module, "garbage", gcstate->garbage) < 0) {
557
0
        return -1;
558
0
    }
559
4
    assert(gcstate->callbacks != NULL);
560
4
    if (PyModule_AddObjectRef(module, "callbacks", gcstate->callbacks) < 0) {
561
0
        return -1;
562
0
    }
563
564
20
#define ADD_INT(NAME) if (PyModule_AddIntConstant(module, #NAME, _PyGC_ ## NAME) < 0) { return -1; }
565
4
    ADD_INT(DEBUG_STATS);
566
4
    ADD_INT(DEBUG_COLLECTABLE);
567
4
    ADD_INT(DEBUG_UNCOLLECTABLE);
568
4
    ADD_INT(DEBUG_SAVEALL);
569
4
    ADD_INT(DEBUG_LEAK);
570
4
#undef ADD_INT
571
4
    return 0;
572
4
}
573
574
static PyModuleDef_Slot gcmodule_slots[] = {
575
    _Py_ABI_SLOT,
576
    {Py_mod_exec, gcmodule_exec},
577
    {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
578
    {Py_mod_gil, Py_MOD_GIL_NOT_USED},
579
    {0, NULL}
580
};
581
582
static struct PyModuleDef gcmodule = {
583
    PyModuleDef_HEAD_INIT,
584
    .m_name = "gc",
585
    .m_doc = gc__doc__,
586
    .m_size = 0,  // per interpreter state, see: get_gc_state()
587
    .m_methods = GcMethods,
588
    .m_slots = gcmodule_slots
589
};
590
591
PyMODINIT_FUNC
592
PyInit_gc(void)
593
4
{
594
4
    return PyModuleDef_Init(&gcmodule);
595
4
}