Skip to content

Commit a3089e5

Browse files
committed
wip: fix OOM error handling, add ifdef toggles
1 parent 942f77d commit a3089e5

File tree

2 files changed

+70
-27
lines changed

2 files changed

+70
-27
lines changed

Include/internal/pycore_gc.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,9 +291,9 @@ enum _GCPhase {
291291
};
292292

293293
// if true, enable GC timing statistics
294-
#define WITH_GC_TIMING_STATS 0
294+
//#define WITH_GC_TIMING_STATS 1
295295

296-
#if WITH_GC_TIMING_STATS
296+
#ifdef WITH_GC_TIMING_STATS
297297

298298
#define QUANTILE_COUNT 5
299299
#define MARKER_COUNT (QUANTILE_COUNT * 3 + 2)
@@ -353,7 +353,7 @@ struct _gc_runtime_state {
353353

354354
int freeze_used;
355355

356-
#if WITH_GC_TIMING_STATS
356+
#ifdef WITH_GC_TIMING_STATS
357357
/* state for GC timing statistics */
358358
struct gc_timing_state timing_state;
359359
#endif

Python/gc_free_threading.c

Lines changed: 67 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@
1818
#include "pydtrace.h"
1919
#include "pycore_uniqueid.h" // _PyObject_MergeThreadLocalRefcounts()
2020

21+
22+
// enable the "mark alive" pass of GC
23+
#define GC_ENABLE_MARK_ALIVE 1
24+
25+
// include addtional roots in "mark alive" pass
26+
//#define GC_MARK_ALIVE_EXTRA_ROOTS 1
27+
28+
// include Python stacks as set of known roots
29+
//#define GC_MARK_ALIVE_STACKS 1
30+
31+
2132
#ifdef Py_GIL_DISABLED
2233

2334
typedef struct _gc_runtime_state GCState;
@@ -34,7 +45,7 @@ typedef struct _gc_runtime_state GCState;
3445
// Automatically choose the generation that needs collecting.
3546
#define GENERATION_AUTO (-1)
3647

37-
#if WITH_GC_TIMING_STATS
48+
#ifdef WITH_GC_TIMING_STATS
3849

3950
static void p2engine_init(p2_engine* engine, const double quantiles[QUANTILE_COUNT]) {
4051
engine->count = 0;
@@ -312,11 +323,13 @@ gc_is_alive(PyObject *op)
312323
return gc_has_bit(op, _PyGC_BITS_ALIVE);
313324
}
314325

326+
#ifdef GC_ENABLE_MARK_ALIVE
315327
static void
316328
gc_set_alive(PyObject *op)
317329
{
318330
gc_set_bit(op, _PyGC_BITS_ALIVE);
319331
}
332+
#endif
320333

321334
static void
322335
gc_clear_alive(PyObject *op)
@@ -584,8 +597,9 @@ gc_visit_thread_stacks(PyInterpreterState *interp)
584597
_Py_FOR_EACH_TSTATE_END(interp);
585598
}
586599

600+
#ifdef GC_ENABLE_MARK_ALIVE
587601
static bool
588-
mark_stack_push(_PyObjectStack *stack, PyObject *op)
602+
mark_alive_stack_push(_PyObjectStack *stack, PyObject *op)
589603
{
590604
if (op == NULL) {
591605
return true;
@@ -599,7 +613,9 @@ mark_stack_push(_PyObjectStack *stack, PyObject *op)
599613
return true;
600614
}
601615

602-
static inline void
616+
617+
#ifdef GC_MARK_ALIVE_STACKS
618+
static bool
603619
gc_visit_stackref_mark_alive(_PyObjectStack *stack, _PyStackRef stackref)
604620
{
605621
// Note: we MUST check that it is deferred before checking the rest.
@@ -608,12 +624,15 @@ gc_visit_stackref_mark_alive(_PyObjectStack *stack, _PyStackRef stackref)
608624
if (PyStackRef_IsDeferred(stackref) && !PyStackRef_IsNull(stackref)) {
609625
PyObject *obj = PyStackRef_AsPyObjectBorrow(stackref);
610626
if (_PyObject_GC_IS_TRACKED(obj) && !gc_is_frozen(obj)) {
611-
mark_stack_push(stack, obj);
627+
if (!mark_alive_stack_push(stack, obj)) {
628+
return false;
629+
}
612630
}
613631
}
632+
return true;
614633
}
615634

616-
static void
635+
static bool
617636
gc_visit_thread_stacks_mark_alive(PyInterpreterState *interp, _PyObjectStack *stack)
618637
{
619638
_Py_FOR_EACH_TSTATE_BEGIN(interp, p) {
@@ -625,14 +644,21 @@ gc_visit_thread_stacks_mark_alive(PyInterpreterState *interp, _PyObjectStack *st
625644

626645
PyCodeObject *co = (PyCodeObject *)executable;
627646
int max_stack = co->co_nlocalsplus + co->co_stacksize;
628-
gc_visit_stackref(f->f_executable);
647+
if (!gc_visit_stackref_mark_alive(stack, f->f_executable)) {
648+
return false;
649+
}
629650
for (int i = 0; i < max_stack; i++) {
630-
gc_visit_stackref_mark_alive(stack, f->localsplus[i]);
651+
if (!gc_visit_stackref_mark_alive(stack, f->localsplus[i])) {
652+
return false;
653+
}
631654
}
632655
}
633656
}
634657
_Py_FOR_EACH_TSTATE_END(interp);
658+
return true;
635659
}
660+
#endif // GC_MARK_ALIVE_STACKS
661+
#endif // GC_ENABLE_MARK_ALIVE
636662

637663
static void
638664
queue_untracked_obj_decref(PyObject *op, struct collection_state *state)
@@ -974,7 +1000,7 @@ scan_heap_visitor(const mi_heap_t *heap, const mi_heap_area_t *area,
9741000
static int
9751001
move_legacy_finalizer_reachable(struct collection_state *state);
9761002

977-
#if WITH_GC_TIMING_STATS
1003+
#ifdef WITH_GC_TIMING_STATS
9781004
FILE *gc_log;
9791005
static void
9801006
print_gc_times(GCState *gcstate)
@@ -989,6 +1015,7 @@ print_gc_times(GCState *gcstate)
9891015
}
9901016
#endif // WITH_GC_TIMING_STATS
9911017

1018+
#ifdef GC_ENABLE_MARK_ALIVE
9921019
static int
9931020
visit_propagate_alive(PyObject *op, _PyObjectStack *stack)
9941021
{
@@ -1022,7 +1049,7 @@ static int
10221049
mark_root_reachable(PyInterpreterState *interp,
10231050
struct collection_state *state)
10241051
{
1025-
#if WITH_GC_TIMING_STATS
1052+
#ifdef WITH_GC_TIMING_STATS
10261053
PyTime_t t1;
10271054
(void)PyTime_PerfCounterRaw(&t1);
10281055
#endif
@@ -1032,29 +1059,45 @@ mark_root_reachable(PyInterpreterState *interp,
10321059
gc_visit_heaps(interp, &validate_alive_bits, &state->base);
10331060
#endif
10341061
_PyObjectStack stack = { NULL };
1035-
gc_visit_thread_stacks_mark_alive(interp, &stack);
1036-
mark_stack_push(&stack, interp->sysdict);
1037-
mark_stack_push(&stack, interp->builtins);
1038-
mark_stack_push(&stack, interp->dict);
1062+
1063+
#define STACK_PUSH(op) \
1064+
if (!mark_alive_stack_push(&stack, op)) { \
1065+
_PyObjectStack_Clear(&stack); \
1066+
return -1; \
1067+
}
1068+
STACK_PUSH(interp->sysdict);
1069+
#ifdef GC_MARK_ALIVE_EXTRA_ROOTS
1070+
STACK_PUSH(interp->builtins);
1071+
STACK_PUSH(interp->dict);
10391072
struct types_state *types = &interp->types;
10401073
for (int i = 0; i < _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES; i++) {
1041-
mark_stack_push(&stack, types->builtins.initialized[i].tp_dict);
1042-
mark_stack_push(&stack, types->builtins.initialized[i].tp_subclasses);
1074+
STACK_PUSH(types->builtins.initialized[i].tp_dict);
1075+
STACK_PUSH(types->builtins.initialized[i].tp_subclasses);
10431076
}
10441077
for (int i = 0; i < _Py_MAX_MANAGED_STATIC_EXT_TYPES; i++) {
1045-
mark_stack_push(&stack, types->for_extensions.initialized[i].tp_dict);
1046-
mark_stack_push(&stack, types->for_extensions.initialized[i].tp_subclasses);
1078+
STACK_PUSH(types->for_extensions.initialized[i].tp_dict);
1079+
STACK_PUSH(types->for_extensions.initialized[i].tp_subclasses);
1080+
}
1081+
#endif
1082+
#ifdef GC_MARK_ALIVE_STACKS
1083+
if (!gc_visit_thread_stacks_mark_alive(interp, &stack)) {
1084+
_PyObjectStack_Clear(&stack);
1085+
return -1;
10471086
}
1087+
#endif
1088+
#undef STACK_PUSH
1089+
10481090
propagate_alive_bits(&stack);
10491091

1050-
#if WITH_GC_TIMING_STATS
1092+
#ifdef WITH_GC_TIMING_STATS
10511093
PyTime_t t2;
10521094
(void)PyTime_PerfCounterRaw(&t2);
10531095
interp->gc.timing_state.gc_mark_time += t2 - t1;
10541096
#endif
10551097

10561098
return 0;
10571099
}
1100+
#endif // GC_ENABLE_MARK_ALIVE
10581101

10591102

10601103
static int
@@ -1245,7 +1288,7 @@ _PyGC_Init(PyInterpreterState *interp)
12451288
return _PyStatus_NO_MEMORY();
12461289
}
12471290

1248-
#if WITH_GC_TIMING_STATS
1291+
#ifdef WITH_GC_TIMING_STATS
12491292
gc_log = fopen("/tmp/gc_timing.log", "a");
12501293
//p2engine_init(&gcstate->timing_state.auto_all, gc_timing_quantiles);
12511294
p2engine_init(&gcstate->timing_state.auto_full, gc_timing_quantiles);
@@ -1630,7 +1673,7 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
16301673

16311674
process_delayed_frees(interp, state);
16321675

1633-
#if 1
1676+
#ifdef GC_ENABLE_MARK_ALIVE
16341677
if (!state->gcstate->freeze_used) {
16351678
// Mark objects reachable from known roots as "alive". These will
16361679
// be ignored for rest of the GC pass.
@@ -1744,7 +1787,7 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason)
17441787
invoke_gc_callback(tstate, "start", generation, 0, 0);
17451788
}
17461789

1747-
#if WITH_GC_TIMING_STATS
1790+
#ifdef WITH_GC_TIMING_STATS
17481791
PyTime_t gc_timing_t1;
17491792
(void)PyTime_PerfCounterRaw(&gc_timing_t1);
17501793
#endif
@@ -1782,7 +1825,7 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason)
17821825
n+m, n, state.long_lived_total, d);
17831826
}
17841827

1785-
#if WITH_GC_TIMING_STATS
1828+
#ifdef WITH_GC_TIMING_STATS
17861829
PyTime_t gc_timing_t2, dt;
17871830
(void)PyTime_PerfCounterRaw(&gc_timing_t2);
17881831
dt = gc_timing_t2 - gc_timing_t1;
@@ -1831,7 +1874,7 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason)
18311874
}
18321875
#endif
18331876

1834-
#if WITH_GC_TIMING_STATS
1877+
#ifdef WITH_GC_TIMING_STATS
18351878
fprintf(gc_log, "gc alive %d collected %ld checked %d gc %d\n", num_alive, m, num_checked, num_gc);
18361879
fflush(gc_log);
18371880
#endif
@@ -2165,7 +2208,7 @@ _PyGC_Fini(PyInterpreterState *interp)
21652208
Py_CLEAR(gcstate->garbage);
21662209
Py_CLEAR(gcstate->callbacks);
21672210

2168-
#if WITH_GC_TIMING_STATS
2211+
#ifdef WITH_GC_TIMING_STATS
21692212
print_gc_times(gcstate);
21702213
#if 0 // no generations so all are full collections
21712214
for (int i = 0; i < QUANTILE_COUNT; i++) {

0 commit comments

Comments
 (0)