Coverage Report

Created: 2026-06-01 06:14

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