py/gc: Reorder static functions for clarity.
- 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:
committed by
Damien George
parent
fd0e529a47
commit
990f50fbb8
175
py/gc.c
175
py/gc.c
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user