Skip to content

Commit

Permalink
py/gc: Reorder static functions for clarity.
Browse files Browse the repository at this point in the history
- Renamed gc_sweep to gc_sweep_free_blocks.
- Call gc_sweep_run_finalisers from top level.
- Reordered the gc static functions to be in approximate
  runtime sequence (with forward declarations) rather than
  in declaration order.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <[email protected]>
  • Loading branch information
projectgus authored and dpgeorge committed Feb 3, 2025
1 parent fd0e529 commit 990f50f
Showing 1 changed file with 93 additions and 82 deletions.
175 changes: 93 additions & 82 deletions py/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,18 @@
#define GC_EXIT()
#endif

// Static functions for individual steps of the GC mark/sweep sequence
static void gc_collect_start_common(void);
static void *gc_get_ptr(void **ptrs, int i);
#if MICROPY_GC_SPLIT_HEAP
static void gc_mark_subtree(mp_state_mem_area_t *area, size_t block);
#else
static void gc_mark_subtree(size_t block);
#endif
static void gc_deal_with_stack_overflow(void);
static void gc_sweep_run_finalisers(void);
static void gc_sweep_free_blocks(void);

// TODO waste less memory; currently requires that all entries in alloc_table have a corresponding block in pool
static void gc_setup_area(mp_state_mem_area_t *area, void *start, void *end) {
// calculate parameters for GC (T=total, A=alloc table, F=finaliser table, P=pool; all in bytes):
Expand Down Expand Up @@ -379,6 +391,64 @@ static inline mp_state_mem_area_t *gc_get_ptr_area(const void *ptr) {
#endif
#endif

void gc_collect_start(void) {
gc_collect_start_common();
#if MICROPY_GC_ALLOC_THRESHOLD
MP_STATE_MEM(gc_alloc_amount) = 0;
#endif

// Trace root pointers. This relies on the root pointers being organised
// correctly in the mp_state_ctx structure. We scan nlr_top, dict_locals,
// dict_globals, then the root pointer section of mp_state_vm.
void **ptrs = (void **)(void *)&mp_state_ctx;
size_t root_start = offsetof(mp_state_ctx_t, thread.dict_locals);
size_t root_end = offsetof(mp_state_ctx_t, vm.qstr_last_chunk);
gc_collect_root(ptrs + root_start / sizeof(void *), (root_end - root_start) / sizeof(void *));

#if MICROPY_ENABLE_PYSTACK
// Trace root pointers from the Python stack.
ptrs = (void **)(void *)MP_STATE_THREAD(pystack_start);
gc_collect_root(ptrs, (MP_STATE_THREAD(pystack_cur) - MP_STATE_THREAD(pystack_start)) / sizeof(void *));
#endif
}

static void gc_collect_start_common(void) {
GC_ENTER();
assert((MP_STATE_THREAD(gc_lock_depth) & GC_COLLECT_FLAG) == 0);
MP_STATE_THREAD(gc_lock_depth) |= GC_COLLECT_FLAG;
MP_STATE_MEM(gc_stack_overflow) = 0;
}

void gc_collect_root(void **ptrs, size_t len) {
#if !MICROPY_GC_SPLIT_HEAP
mp_state_mem_area_t *area = &MP_STATE_MEM(area);
#endif
for (size_t i = 0; i < len; i++) {
MICROPY_GC_HOOK_LOOP(i);
void *ptr = gc_get_ptr(ptrs, i);
#if MICROPY_GC_SPLIT_HEAP
mp_state_mem_area_t *area = gc_get_ptr_area(ptr);
if (!area) {
continue;
}
#else
if (!VERIFY_PTR(ptr)) {
continue;
}
#endif
size_t block = BLOCK_FROM_PTR(area, ptr);
if (ATB_GET_KIND(area, block) == AT_HEAD) {
// An unmarked head: mark it, and mark all its children
ATB_HEAD_TO_MARK(area, block);
#if MICROPY_GC_SPLIT_HEAP
gc_mark_subtree(area, block);
#else
gc_mark_subtree(block);
#endif
}
}
}

// Take the given block as the topmost block on the stack. Check all it's
// children: mark the unmarked child blocks and put those newly marked
// blocks on the stack. When all children have been checked, pop off the
Expand Down Expand Up @@ -457,6 +527,25 @@ static void gc_mark_subtree(size_t block)
}
}

void gc_sweep_all(void) {
gc_collect_start_common();
gc_collect_end();
}

void gc_collect_end(void) {
gc_deal_with_stack_overflow();
gc_sweep_run_finalisers();
gc_sweep_free_blocks();
#if MICROPY_GC_SPLIT_HEAP
MP_STATE_MEM(gc_last_free_area) = &MP_STATE_MEM(area);
#endif
for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) {
area->gc_last_free_atb_index = 0;
}
MP_STATE_THREAD(gc_lock_depth) &= ~GC_COLLECT_FLAG;
GC_EXIT();
}

static void gc_deal_with_stack_overflow(void) {
while (MP_STATE_MEM(gc_stack_overflow)) {
MP_STATE_MEM(gc_stack_overflow) = 0;
Expand Down Expand Up @@ -520,18 +609,16 @@ static void gc_sweep_run_finalisers(void) {
#endif // MICROPY_ENABLE_FINALISER
}

static void gc_sweep(void) {
// Free unmarked heads and their tails
static void gc_sweep_free_blocks(void) {
#if MICROPY_PY_GC_COLLECT_RETVAL
MP_STATE_MEM(gc_collected) = 0;
#endif
// free unmarked heads and their tails
int free_tail = 0;
#if MICROPY_GC_SPLIT_HEAP_AUTO
mp_state_mem_area_t *prev_area = NULL;
#endif

gc_sweep_run_finalisers();

for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) {
size_t last_used_block = 0;
assert(area->gc_last_used_block <= area->gc_alloc_table_byte_len * BLOCKS_PER_ATB);
Expand All @@ -541,7 +628,7 @@ static void gc_sweep(void) {
switch (ATB_GET_KIND(area, block)) {
case AT_HEAD:
free_tail = 1;
DEBUG_printf("gc_sweep(%p)\n", (void *)PTR_FROM_BLOCK(area, block));
DEBUG_printf("gc_sweep_free_blocks(%p)\n", (void *)PTR_FROM_BLOCK(area, block));
#if MICROPY_PY_GC_COLLECT_RETVAL
MP_STATE_MEM(gc_collected)++;
#endif
Expand Down Expand Up @@ -572,7 +659,7 @@ static void gc_sweep(void) {
#if MICROPY_GC_SPLIT_HEAP_AUTO
// Free any empty area, aside from the first one
if (last_used_block == 0 && prev_area != NULL) {
DEBUG_printf("gc_sweep free empty area %p\n", area);
DEBUG_printf("gc_sweep_free_blocks free empty area %p\n", area);
NEXT_AREA(prev_area) = NEXT_AREA(area);
MP_PLAT_FREE_HEAP(area);
area = prev_area;
Expand All @@ -582,34 +669,6 @@ static void gc_sweep(void) {
}
}

static void gc_collect_start_common(void) {
GC_ENTER();
assert((MP_STATE_THREAD(gc_lock_depth) & GC_COLLECT_FLAG) == 0);
MP_STATE_THREAD(gc_lock_depth) |= GC_COLLECT_FLAG;
MP_STATE_MEM(gc_stack_overflow) = 0;
}

void gc_collect_start(void) {
gc_collect_start_common();
#if MICROPY_GC_ALLOC_THRESHOLD
MP_STATE_MEM(gc_alloc_amount) = 0;
#endif

// Trace root pointers. This relies on the root pointers being organised
// correctly in the mp_state_ctx structure. We scan nlr_top, dict_locals,
// dict_globals, then the root pointer section of mp_state_vm.
void **ptrs = (void **)(void *)&mp_state_ctx;
size_t root_start = offsetof(mp_state_ctx_t, thread.dict_locals);
size_t root_end = offsetof(mp_state_ctx_t, vm.qstr_last_chunk);
gc_collect_root(ptrs + root_start / sizeof(void *), (root_end - root_start) / sizeof(void *));

#if MICROPY_ENABLE_PYSTACK
// Trace root pointers from the Python stack.
ptrs = (void **)(void *)MP_STATE_THREAD(pystack_start);
gc_collect_root(ptrs, (MP_STATE_THREAD(pystack_cur) - MP_STATE_THREAD(pystack_start)) / sizeof(void *));
#endif
}

// Address sanitizer needs to know that the access to ptrs[i] must always be
// considered OK, even if it's a load from an address that would normally be
// prohibited (due to being undefined, in a red zone, etc).
Expand All @@ -625,54 +684,6 @@ static void *gc_get_ptr(void **ptrs, int i) {
return ptrs[i];
}

void gc_collect_root(void **ptrs, size_t len) {
#if !MICROPY_GC_SPLIT_HEAP
mp_state_mem_area_t *area = &MP_STATE_MEM(area);
#endif
for (size_t i = 0; i < len; i++) {
MICROPY_GC_HOOK_LOOP(i);
void *ptr = gc_get_ptr(ptrs, i);
#if MICROPY_GC_SPLIT_HEAP
mp_state_mem_area_t *area = gc_get_ptr_area(ptr);
if (!area) {
continue;
}
#else
if (!VERIFY_PTR(ptr)) {
continue;
}
#endif
size_t block = BLOCK_FROM_PTR(area, ptr);
if (ATB_GET_KIND(area, block) == AT_HEAD) {
// An unmarked head: mark it, and mark all its children
ATB_HEAD_TO_MARK(area, block);
#if MICROPY_GC_SPLIT_HEAP
gc_mark_subtree(area, block);
#else
gc_mark_subtree(block);
#endif
}
}
}

void gc_collect_end(void) {
gc_deal_with_stack_overflow();
gc_sweep();
#if MICROPY_GC_SPLIT_HEAP
MP_STATE_MEM(gc_last_free_area) = &MP_STATE_MEM(area);
#endif
for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) {
area->gc_last_free_atb_index = 0;
}
MP_STATE_THREAD(gc_lock_depth) &= ~GC_COLLECT_FLAG;
GC_EXIT();
}

void gc_sweep_all(void) {
gc_collect_start_common();
gc_collect_end();
}

void gc_info(gc_info_t *info) {
GC_ENTER();
info->total = 0;
Expand Down

0 comments on commit 990f50f

Please sign in to comment.