From fe120484b67bdc114dbbc2b9bd913fab98c82778 Mon Sep 17 00:00:00 2001 From: Laurens Valk Date: Tue, 26 Oct 2021 10:47:04 +0200 Subject: [PATCH] py/gc: Add hook to run code during time consuming GC operations. This makes it possible for cooperative multitasking systems to keep running event loops during garbage collector operations. For example, this can be used to ensure that a motor control loop runs approximately each 5 ms. Without this hook, the loop time can jump to about 15 ms. Addresses #3475. Signed-off-by: Laurens Valk --- py/gc.c | 5 +++++ py/mpconfig.h | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/py/gc.c b/py/gc.c index 8284c435ba..a01d81abdc 100644 --- a/py/gc.c +++ b/py/gc.c @@ -213,6 +213,7 @@ STATIC void gc_mark_subtree(size_t block) { // Start with the block passed in the argument. size_t sp = 0; for (;;) { + MICROPY_GC_HOOK_LOOP // work out number of consecutive blocks in the chain starting with this one size_t n_blocks = 0; do { @@ -222,6 +223,7 @@ STATIC void gc_mark_subtree(size_t block) { // check this block's children void **ptrs = (void **)PTR_FROM_BLOCK(block); for (size_t i = n_blocks * BYTES_PER_BLOCK / sizeof(void *); i > 0; i--, ptrs++) { + MICROPY_GC_HOOK_LOOP void *ptr = *ptrs; if (VERIFY_PTR(ptr)) { // Mark and push this pointer @@ -255,6 +257,7 @@ STATIC void gc_deal_with_stack_overflow(void) { // scan entire memory looking for blocks which have been marked but not their children for (size_t block = 0; block < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; block++) { + MICROPY_GC_HOOK_LOOP // trace (again) if mark bit set if (ATB_GET_KIND(block) == AT_MARK) { gc_mark_subtree(block); @@ -270,6 +273,7 @@ STATIC void gc_sweep(void) { // free unmarked heads and their tails int free_tail = 0; for (size_t block = 0; block < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; block++) { + MICROPY_GC_HOOK_LOOP switch (ATB_GET_KIND(block)) { case AT_HEAD: #if MICROPY_ENABLE_FINALISER @@ -354,6 +358,7 @@ static void *gc_get_ptr(void **ptrs, int i) { void gc_collect_root(void **ptrs, size_t len) { for (size_t i = 0; i < len; i++) { + MICROPY_GC_HOOK_LOOP void *ptr = gc_get_ptr(ptrs, i); if (VERIFY_PTR(ptr)) { size_t block = BLOCK_FROM_PTR(ptr); diff --git a/py/mpconfig.h b/py/mpconfig.h index 596bab8d89..06c46b8c73 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -602,6 +602,11 @@ #define MICROPY_ENABLE_GC (0) #endif +// Hook to run code during time consuming garbage collector operations +#ifndef MICROPY_GC_HOOK_LOOP +#define MICROPY_GC_HOOK_LOOP +#endif + // Whether to enable finalisers in the garbage collector (ie call __del__) #ifndef MICROPY_ENABLE_FINALISER #define MICROPY_ENABLE_FINALISER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)