py/gc: Reorder static functions for clarity.
Some checks failed
JavaScript code lint and formatting with Biome / eslint (push) Has been cancelled
Check code formatting / code-formatting (push) Has been cancelled
Check spelling with codespell / codespell (push) Has been cancelled
Build docs / build (push) Has been cancelled
Check examples / embedding (push) Has been cancelled
Package mpremote / build (push) Has been cancelled
.mpy file format and tools / test (push) Has been cancelled
Build ports metadata / build (push) Has been cancelled
cc3200 port / build (push) Has been cancelled
esp32 port / build_idf (esp32_build_cmod_spiram_s2) (push) Has been cancelled
esp32 port / build_idf (esp32_build_s3_c3) (push) Has been cancelled
esp8266 port / build (push) Has been cancelled
mimxrt port / build (push) Has been cancelled
nrf port / build (push) Has been cancelled
powerpc port / build (push) Has been cancelled
qemu port / build_and_test_arm (push) Has been cancelled
qemu port / build_and_test_rv32 (push) Has been cancelled
renesas-ra port / build_renesas_ra_board (push) Has been cancelled
rp2 port / build (push) Has been cancelled
samd port / build (push) Has been cancelled
stm32 port / build_stm32 (stm32_misc_build) (push) Has been cancelled
stm32 port / build_stm32 (stm32_nucleo_build) (push) Has been cancelled
stm32 port / build_stm32 (stm32_pyb_build) (push) Has been cancelled
unix port / minimal (push) Has been cancelled
unix port / reproducible (push) Has been cancelled
unix port / standard (push) Has been cancelled
unix port / standard_v2 (push) Has been cancelled
unix port / coverage (push) Has been cancelled
unix port / coverage_32bit (push) Has been cancelled
unix port / nanbox (push) Has been cancelled
unix port / float (push) Has been cancelled
unix port / stackless_clang (push) Has been cancelled
unix port / float_clang (push) Has been cancelled
unix port / settrace (push) Has been cancelled
unix port / settrace_stackless (push) Has been cancelled
unix port / macos (push) Has been cancelled
unix port / qemu_mips (push) Has been cancelled
unix port / qemu_arm (push) Has been cancelled
unix port / qemu_riscv64 (push) Has been cancelled
webassembly port / build (push) Has been cancelled
windows port / build-vs (Debug, x64, windows-2022, dev, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Debug, x64, windows-latest, dev, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Debug, x86, windows-2022, dev, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Debug, x86, windows-latest, dev, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Release, x64, windows-2019, dev, 2019, [16, 17)) (push) Has been cancelled
windows port / build-vs (Release, x64, windows-2019, standard, 2019, [16, 17)) (push) Has been cancelled
windows port / build-vs (Release, x64, windows-2022, dev, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Release, x64, windows-2022, standard, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Release, x64, windows-latest, dev, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Release, x64, windows-latest, standard, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Release, x86, windows-2019, dev, 2019, [16, 17)) (push) Has been cancelled
windows port / build-vs (Release, x86, windows-2019, standard, 2019, [16, 17)) (push) Has been cancelled
windows port / build-vs (Release, x86, windows-2022, dev, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Release, x86, windows-2022, standard, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Release, x86, windows-latest, dev, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Release, x86, windows-latest, standard, 2017, [15, 16)) (push) Has been cancelled
windows port / build-mingw (i686, mingw32, dev) (push) Has been cancelled
windows port / build-mingw (i686, mingw32, standard) (push) Has been cancelled
windows port / build-mingw (x86_64, mingw64, dev) (push) Has been cancelled
windows port / build-mingw (x86_64, mingw64, standard) (push) Has been cancelled
windows port / cross-build-on-linux (push) Has been cancelled
zephyr port / build (push) Has been cancelled
Python code lint and formatting with ruff / ruff (push) Has been cancelled

- 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 <angus@redyak.com.au>
This commit is contained in:
Angus Gratton
2025-01-29 10:10:10 +11:00
committed by Damien George
parent fd0e529a47
commit 990f50fbb8

175
py/gc.c
View File

@@ -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):
@@ -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
@@ -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;
@@ -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);
@@ -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
@@ -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;
@@ -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).
@@ -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;