summaryrefslogtreecommitdiffstatshomepage
path: root/py
diff options
context:
space:
mode:
authorAngus Gratton <angus@redyak.com.au>2025-01-29 10:10:10 +1100
committerDamien George <damien@micropython.org>2025-02-03 15:02:02 +1100
commit990f50fbb829e41ce275d942c056bcd3f4b857df (patch)
treedc8a8ebb762317ab408442be17d0c1dde5a88c52 /py
parentfd0e529a476650a070046a38b7ba7e253b65a77a (diff)
downloadmicropython-990f50fbb829e41ce275d942c056bcd3f4b857df.tar.gz
micropython-990f50fbb829e41ce275d942c056bcd3f4b857df.zip
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>
Diffstat (limited to 'py')
-rw-r--r--py/gc.c175
1 files changed, 93 insertions, 82 deletions
diff --git a/py/gc.c b/py/gc.c
index 6a3f48ac3c..eda63187b2 100644
--- a/py/gc.c
+++ b/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;