Coverage Report

Created: 2025-10-12 06:48

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