Coverage Report

Created: 2025-07-04 06:49

/src/cpython/Modules/gcmodule.c
Line
Count
Source (jump to first uncovered line)
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
#include "pycore_tuple.h"       // _PyTuple_FromArray()
12
13
typedef struct _gc_runtime_state GCState;
14
15
static GCState *
16
get_gc_state(void)
17
0
{
18
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
19
0
    return &interp->gc;
20
0
}
21
22
/*[clinic input]
23
module gc
24
[clinic start generated code]*/
25
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=b5c9690ecc842d79]*/
26
#include "clinic/gcmodule.c.h"
27
28
/*[clinic input]
29
gc.enable
30
31
Enable automatic garbage collection.
32
[clinic start generated code]*/
33
34
static PyObject *
35
gc_enable_impl(PyObject *module)
36
/*[clinic end generated code: output=45a427e9dce9155c input=81ac4940ca579707]*/
37
0
{
38
0
    PyGC_Enable();
39
0
    Py_RETURN_NONE;
40
0
}
41
42
/*[clinic input]
43
gc.disable
44
45
Disable automatic garbage collection.
46
[clinic start generated code]*/
47
48
static PyObject *
49
gc_disable_impl(PyObject *module)
50
/*[clinic end generated code: output=97d1030f7aa9d279 input=8c2e5a14e800d83b]*/
51
0
{
52
0
    PyGC_Disable();
53
0
    Py_RETURN_NONE;
54
0
}
55
56
/*[clinic input]
57
gc.isenabled -> bool
58
59
Returns true if automatic garbage collection is enabled.
60
[clinic start generated code]*/
61
62
static int
63
gc_isenabled_impl(PyObject *module)
64
/*[clinic end generated code: output=1874298331c49130 input=30005e0422373b31]*/
65
0
{
66
0
    return PyGC_IsEnabled();
67
0
}
68
69
/*[clinic input]
70
gc.collect -> Py_ssize_t
71
72
    generation: int(c_default="NUM_GENERATIONS - 1") = 2
73
74
Run the garbage collector.
75
76
With no arguments, run a full collection.  The optional argument
77
may be an integer specifying which generation to collect.  A ValueError
78
is raised if the generation number is invalid.
79
80
The number of unreachable objects is returned.
81
[clinic start generated code]*/
82
83
static Py_ssize_t
84
gc_collect_impl(PyObject *module, int generation)
85
/*[clinic end generated code: output=b697e633043233c7 input=40720128b682d879]*/
86
0
{
87
0
    PyThreadState *tstate = _PyThreadState_GET();
88
89
0
    if (generation < 0 || generation >= NUM_GENERATIONS) {
90
0
        _PyErr_SetString(tstate, PyExc_ValueError, "invalid generation");
91
0
        return -1;
92
0
    }
93
94
0
    return _PyGC_Collect(tstate, generation, _Py_GC_REASON_MANUAL);
95
0
}
96
97
/*[clinic input]
98
gc.set_debug
99
100
    flags: int
101
        An integer that can have the following bits turned on:
102
          DEBUG_STATS - Print statistics during collection.
103
          DEBUG_COLLECTABLE - Print collectable objects found.
104
          DEBUG_UNCOLLECTABLE - Print unreachable but uncollectable objects
105
            found.
106
          DEBUG_SAVEALL - Save objects to gc.garbage rather than freeing them.
107
          DEBUG_LEAK - Debug leaking programs (everything but STATS).
108
    /
109
110
Set the garbage collection debugging flags.
111
112
Debugging information is written to sys.stderr.
113
[clinic start generated code]*/
114
115
static PyObject *
116
gc_set_debug_impl(PyObject *module, int flags)
117
/*[clinic end generated code: output=7c8366575486b228 input=5e5ce15e84fbed15]*/
118
0
{
119
0
    GCState *gcstate = get_gc_state();
120
0
    gcstate->debug = flags;
121
0
    Py_RETURN_NONE;
122
0
}
123
124
/*[clinic input]
125
gc.get_debug -> int
126
127
Get the garbage collection debugging flags.
128
[clinic start generated code]*/
129
130
static int
131
gc_get_debug_impl(PyObject *module)
132
/*[clinic end generated code: output=91242f3506cd1e50 input=91a101e1c3b98366]*/
133
0
{
134
0
    GCState *gcstate = get_gc_state();
135
0
    return gcstate->debug;
136
0
}
137
138
/*[clinic input]
139
gc.set_threshold
140
141
    threshold0: int
142
    [
143
    threshold1: int
144
    [
145
    threshold2: int
146
    ]
147
    ]
148
    /
149
150
Set the collection thresholds (the collection frequency).
151
152
Setting 'threshold0' to zero disables collection.
153
[clinic start generated code]*/
154
155
static PyObject *
156
gc_set_threshold_impl(PyObject *module, int threshold0, int group_right_1,
157
                      int threshold1, int group_right_2, int threshold2)
158
/*[clinic end generated code: output=2e3c7c7dd59060f3 input=0d9612db50984eec]*/
159
0
{
160
0
    GCState *gcstate = get_gc_state();
161
162
0
    gcstate->young.threshold = threshold0;
163
0
    if (group_right_1) {
164
0
        gcstate->old[0].threshold = threshold1;
165
0
    }
166
0
    if (group_right_2) {
167
0
        gcstate->old[1].threshold = threshold2;
168
0
    }
169
0
    Py_RETURN_NONE;
170
0
}
171
172
/*[clinic input]
173
gc.get_threshold
174
175
Return the current collection thresholds.
176
[clinic start generated code]*/
177
178
static PyObject *
179
gc_get_threshold_impl(PyObject *module)
180
/*[clinic end generated code: output=7902bc9f41ecbbd8 input=286d79918034d6e6]*/
181
0
{
182
0
    GCState *gcstate = get_gc_state();
183
0
    return Py_BuildValue("(iii)",
184
0
                         gcstate->young.threshold,
185
0
                         gcstate->old[0].threshold,
186
0
                         0);
187
0
}
188
189
/*[clinic input]
190
gc.get_count
191
192
Return a three-tuple of the current collection counts.
193
[clinic start generated code]*/
194
195
static PyObject *
196
gc_get_count_impl(PyObject *module)
197
/*[clinic end generated code: output=354012e67b16398f input=a392794a08251751]*/
198
0
{
199
0
    GCState *gcstate = get_gc_state();
200
201
#ifdef Py_GIL_DISABLED
202
    _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET();
203
    struct _gc_thread_state *gc = &tstate->gc;
204
205
    // Flush the local allocation count to the global count
206
    _Py_atomic_add_int(&gcstate->young.count, (int)gc->alloc_count);
207
    gc->alloc_count = 0;
208
#endif
209
210
0
    return Py_BuildValue("(iii)",
211
0
                         gcstate->young.count,
212
0
                         gcstate->old[gcstate->visited_space].count,
213
0
                         gcstate->old[gcstate->visited_space^1].count);
214
0
}
215
216
/*[clinic input]
217
gc.get_referrers
218
219
    *objs: tuple
220
221
Return the list of objects that directly refer to any of 'objs'.
222
[clinic start generated code]*/
223
224
static PyObject *
225
gc_get_referrers_impl(PyObject *module, PyObject *objs)
226
/*[clinic end generated code: output=929d6dff26f609b9 input=9102be7ebee69ee3]*/
227
0
{
228
0
    if (PySys_Audit("gc.get_referrers", "(O)", objs) < 0) {
229
0
        return NULL;
230
0
    }
231
232
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
233
0
    return _PyGC_GetReferrers(interp, objs);
234
0
}
235
236
/* Append obj to list; return true if error (out of memory), false if OK. */
237
static int
238
referentsvisit(PyObject *obj, void *arg)
239
0
{
240
0
    PyObject *list = arg;
241
0
    return PyList_Append(list, obj) < 0;
242
0
}
243
244
static int
245
append_referrents(PyObject *result, PyObject *args)
246
0
{
247
0
    for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(args); i++) {
248
0
        PyObject *obj = PyTuple_GET_ITEM(args, i);
249
0
        if (!_PyObject_IS_GC(obj)) {
250
0
            continue;
251
0
        }
252
253
0
        traverseproc traverse = Py_TYPE(obj)->tp_traverse;
254
0
        if (!traverse) {
255
0
            continue;
256
0
        }
257
0
        if (traverse(obj, referentsvisit, result)) {
258
0
            return -1;
259
0
        }
260
0
    }
261
0
    return 0;
262
0
}
263
264
/*[clinic input]
265
gc.get_referents
266
267
    *objs: tuple
268
269
Return the list of objects that are directly referred to by 'objs'.
270
[clinic start generated code]*/
271
272
static PyObject *
273
gc_get_referents_impl(PyObject *module, PyObject *objs)
274
/*[clinic end generated code: output=6dfde40cd1588e1d input=55c078a6d0248fe0]*/
275
0
{
276
0
    if (PySys_Audit("gc.get_referents", "(O)", objs) < 0) {
277
0
        return NULL;
278
0
    }
279
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
280
0
    PyObject *result = PyList_New(0);
281
282
0
    if (result == NULL) {
283
0
        return NULL;
284
0
    }
285
286
    // NOTE: stop the world is a no-op in default build
287
0
    _PyEval_StopTheWorld(interp);
288
0
    int err = append_referrents(result, objs);
289
0
    _PyEval_StartTheWorld(interp);
290
291
0
    if (err < 0) {
292
0
        Py_CLEAR(result);
293
0
    }
294
295
0
    return result;
296
0
}
297
298
/*[clinic input]
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=ef7da9df9806754c]*/
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
gc.freeze
418
419
Freeze all current tracked objects and ignore them for future collections.
420
421
This can be used before a POSIX fork() call to make the gc copy-on-write friendly.
422
Note: collection before a POSIX fork() call may free pages for future allocation
423
which can cause copy-on-write.
424
[clinic start generated code]*/
425
426
static PyObject *
427
gc_freeze_impl(PyObject *module)
428
/*[clinic end generated code: output=502159d9cdc4c139 input=b602b16ac5febbe5]*/
429
0
{
430
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
431
0
    _PyGC_Freeze(interp);
432
0
    Py_RETURN_NONE;
433
0
}
434
435
/*[clinic input]
436
gc.unfreeze
437
438
Unfreeze all objects in the permanent generation.
439
440
Put all objects in the permanent generation back into oldest generation.
441
[clinic start generated code]*/
442
443
static PyObject *
444
gc_unfreeze_impl(PyObject *module)
445
/*[clinic end generated code: output=1c15f2043b25e169 input=2dd52b170f4cef6c]*/
446
0
{
447
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
448
0
    _PyGC_Unfreeze(interp);
449
0
    Py_RETURN_NONE;
450
0
}
451
452
/*[clinic input]
453
gc.get_freeze_count -> Py_ssize_t
454
455
Return the number of objects in the permanent generation.
456
[clinic start generated code]*/
457
458
static Py_ssize_t
459
gc_get_freeze_count_impl(PyObject *module)
460
/*[clinic end generated code: output=61cbd9f43aa032e1 input=45ffbc65cfe2a6ed]*/
461
0
{
462
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
463
0
    return _PyGC_GetFreezeCount(interp);
464
0
}
465
466
467
PyDoc_STRVAR(gc__doc__,
468
"This module provides access to the garbage collector for reference cycles.\n"
469
"\n"
470
"enable() -- Enable automatic garbage collection.\n"
471
"disable() -- Disable automatic garbage collection.\n"
472
"isenabled() -- Returns true if automatic collection is enabled.\n"
473
"collect() -- Do a full collection right now.\n"
474
"get_count() -- Return the current collection counts.\n"
475
"get_stats() -- Return list of dictionaries containing per-generation stats.\n"
476
"set_debug() -- Set debugging flags.\n"
477
"get_debug() -- Get debugging flags.\n"
478
"set_threshold() -- Set the collection thresholds.\n"
479
"get_threshold() -- Return the current the collection thresholds.\n"
480
"get_objects() -- Return a list of all objects tracked by the collector.\n"
481
"is_tracked() -- Returns true if a given object is tracked.\n"
482
"is_finalized() -- Returns true if a given object has been already finalized.\n"
483
"get_referrers() -- Return the list of objects that refer to an object.\n"
484
"get_referents() -- Return the list of objects that an object refers to.\n"
485
"freeze() -- Freeze all tracked objects and ignore them for future collections.\n"
486
"unfreeze() -- Unfreeze all objects in the permanent generation.\n"
487
"get_freeze_count() -- Return the number of objects in the permanent generation.\n");
488
489
static PyMethodDef GcMethods[] = {
490
    GC_ENABLE_METHODDEF
491
    GC_DISABLE_METHODDEF
492
    GC_ISENABLED_METHODDEF
493
    GC_SET_DEBUG_METHODDEF
494
    GC_GET_DEBUG_METHODDEF
495
    GC_GET_COUNT_METHODDEF
496
    GC_SET_THRESHOLD_METHODDEF
497
    GC_GET_THRESHOLD_METHODDEF
498
    GC_COLLECT_METHODDEF
499
    GC_GET_OBJECTS_METHODDEF
500
    GC_GET_STATS_METHODDEF
501
    GC_IS_TRACKED_METHODDEF
502
    GC_IS_FINALIZED_METHODDEF
503
    GC_GET_REFERRERS_METHODDEF
504
    GC_GET_REFERENTS_METHODDEF
505
    GC_FREEZE_METHODDEF
506
    GC_UNFREEZE_METHODDEF
507
    GC_GET_FREEZE_COUNT_METHODDEF
508
    {NULL,      NULL}           /* Sentinel */
509
};
510
511
static int
512
gcmodule_exec(PyObject *module)
513
0
{
514
0
    GCState *gcstate = get_gc_state();
515
516
    /* garbage and callbacks are initialized by _PyGC_Init() early in
517
     * interpreter lifecycle. */
518
0
    assert(gcstate->garbage != NULL);
519
0
    if (PyModule_AddObjectRef(module, "garbage", gcstate->garbage) < 0) {
520
0
        return -1;
521
0
    }
522
0
    assert(gcstate->callbacks != NULL);
523
0
    if (PyModule_AddObjectRef(module, "callbacks", gcstate->callbacks) < 0) {
524
0
        return -1;
525
0
    }
526
527
0
#define ADD_INT(NAME) if (PyModule_AddIntConstant(module, #NAME, _PyGC_ ## NAME) < 0) { return -1; }
528
0
    ADD_INT(DEBUG_STATS);
529
0
    ADD_INT(DEBUG_COLLECTABLE);
530
0
    ADD_INT(DEBUG_UNCOLLECTABLE);
531
0
    ADD_INT(DEBUG_SAVEALL);
532
0
    ADD_INT(DEBUG_LEAK);
533
0
#undef ADD_INT
534
0
    return 0;
535
0
}
536
537
static PyModuleDef_Slot gcmodule_slots[] = {
538
    {Py_mod_exec, gcmodule_exec},
539
    {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
540
    {Py_mod_gil, Py_MOD_GIL_NOT_USED},
541
    {0, NULL}
542
};
543
544
static struct PyModuleDef gcmodule = {
545
    PyModuleDef_HEAD_INIT,
546
    .m_name = "gc",
547
    .m_doc = gc__doc__,
548
    .m_size = 0,  // per interpreter state, see: get_gc_state()
549
    .m_methods = GcMethods,
550
    .m_slots = gcmodule_slots
551
};
552
553
PyMODINIT_FUNC
554
PyInit_gc(void)
555
0
{
556
0
    return PyModuleDef_Init(&gcmodule);
557
0
}