diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-12-08 08:28:16 -0700 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-12-08 08:28:16 -0700 |
commit | 96a751d8a5d20b2acea5ae8d10ac3d051466c2c3 (patch) | |
tree | 1ce291df34d78acd6f1619a779abbd4f06134e26 /libcr/coroutine.c | |
parent | afe6542b30def82573e070371281c05dc593a739 (diff) | |
parent | b9ebe41358244caa9334e72ca4e3c8c7a14c86e7 (diff) |
Merge commit 'b9ebe41358244caa9334e72ca4e3c8c7a14c86e7'
Diffstat (limited to 'libcr/coroutine.c')
-rw-r--r-- | libcr/coroutine.c | 112 |
1 files changed, 83 insertions, 29 deletions
diff --git a/libcr/coroutine.c b/libcr/coroutine.c index dbeef12..a947ae9 100644 --- a/libcr/coroutine.c +++ b/libcr/coroutine.c @@ -128,8 +128,17 @@ * no longer exists. */ -#define ALWAYS_INLINE [[gnu::always_inline]] inline -#define NEVER_INLINE [[gnu::noinline]] +/* preprocessor macros ********************************************************/ + +/** Return `n` rounded up to the nearest multiple of `d` */ +#define ROUND_UP(n, d) ( ( ((n)+(d)-1) / (d) ) * (d) ) +#define ARRAY_LEN(arr) (sizeof(arr)/sizeof((arr)[0])) +#define NEXT_POWER_OF_2(x) ((1ULL)<<((sizeof(unsigned long long)*8)-__builtin_clzll(x))) + +#define UNUSED(name) + +#define ALWAYS_INLINE [[gnu::always_inline]] inline +#define NEVER_INLINE [[gnu::noinline]] /* platform support ***********************************************************/ @@ -151,11 +160,16 @@ /* For a signal to be *in* the mask means that the signal is * *blocked*. */ + #define _CR_SIG_SENTINEL SIGURG + #if CONFIG_COROUTINE_GDB + #define _CR_SIG_GDB SIGWINCH + #endif + bool cr_is_in_intrhandler(void) { sigset_t cur_mask; sigfillset(&cur_mask); sigprocmask(0, NULL, &cur_mask); - if (sigismember(&cur_mask, SIGHUP)) + if (sigismember(&cur_mask, _CR_SIG_SENTINEL)) /* Interrupts are disabled, so we cannot be in * an interrupt handler. */ return false; @@ -169,7 +183,7 @@ sigset_t cur_mask; sigfillset(&cur_mask); sigprocmask(0, NULL, &cur_mask); - return !sigismember(&cur_mask, SIGHUP); + return !sigismember(&cur_mask, _CR_SIG_SENTINEL); } static inline void cr_plat_wait_for_interrupt(void) { @@ -187,7 +201,7 @@ sigset_t all, old; sigfillset(&all); sigprocmask(SIG_SETMASK, &all, &old); - return !sigismember(&old, SIGHUP); + return !sigismember(&old, _CR_SIG_SENTINEL); } void _cr_plat_enable_interrupts(void) { assert(!cr_is_in_intrhandler()); @@ -196,6 +210,19 @@ sigemptyset(&zero); sigprocmask(SIG_SETMASK, &zero, NULL); } + #if CONFIG_COROUTINE_GDB + static void _cr_gdb_intrhandler(int UNUSED(sig)) {} + #endif + static void cr_plat_init(void) { + #if CONFIG_COROUTINE_GDB + int r; + struct sigaction action = { + .sa_handler = _cr_gdb_intrhandler, + }; + r = sigaction(_CR_SIG_GDB, &action, NULL); + assert(r == 0); + #endif + } #elif __ARM_ARCH_6M__ && __ARM_EABI__ bool cr_is_in_intrhandler(void) { uint32_t isr_number; @@ -233,6 +260,7 @@ assert(!_cr_plat_are_interrupts_enabled()); asm volatile ("cpsie i"); } + static void cr_plat_init(void) {} #else #error unsupported platform (not __unix__, not __ARM_ARCH_6M__ && __ARM_EABI__) #endif @@ -318,18 +346,14 @@ uintptr_t sp; #endif } cr_plat_jmp_buf; - #if CONIG_COROUTINE_GDB - NEVER_INLINE - #endif static void _cr_plat_setjmp_pre(cr_plat_jmp_buf *env [[gnu::unused]]) { - #if CONIG_COROUTINE_GDB - /* Prevent the call from being optimized away. */ - asm (""); - #endif #if CONFIG_COROUTINE_MEASURE_STACK env->sp = cr_plat_get_sp(); #endif } + #if CONFIG_COROUTINE_MEASURE_STACK + static uintptr_t cr_plat_setjmp_get_sp(cr_plat_jmp_buf *env) { return env->sp; } + #endif /* cr_plat_setjmp *NEEDS* to be a preprocessor macro rather * than a real function, because [[gnu::returns_twice]] * doesn't work. @@ -355,13 +379,6 @@ } #endif -/* preprocessor macros ********************************************************/ - -/** Return `n` rounded up to the nearest multiple of `d` */ -#define ROUND_UP(n, d) ( ( ((n)+(d)-1) / (d) ) * (d) ) -#define ARRAY_LEN(arr) (sizeof(arr)/sizeof((arr)[0])) -#define NEXT_POWER_OF_2(x) ((1ULL)<<((sizeof(unsigned long long)*8)-__builtin_clzll(x))) - /* types **********************************************************************/ enum coroutine_state { @@ -410,8 +427,12 @@ static const uint8_t stack_pattern[] = { /* global variables ***********************************************************/ +static bool coroutine_initialized = false; static cr_plat_jmp_buf coroutine_add_env; static cr_plat_jmp_buf coroutine_main_env; +#if CONFIG_COROUTINE_GDB +static cr_plat_jmp_buf coroutine_gdb_env; +#endif /* * Invariants (and non-invariants): @@ -466,6 +487,28 @@ static inline cid_t coroutine_ringbuf_pop(void) { return coroutine_ringbuf.buf[coroutine_ringbuf.tail++ % ARRAY_LEN(coroutine_ringbuf.buf)]; } +#if CONFIG_COROUTINE_GDB +NEVER_INLINE void cr_gdb_breakpoint(void) { + /* Prevent the call from being optimized away. */ + asm (""); +} +NEVER_INLINE void cr_gdb_readjmp(cr_plat_jmp_buf *env) { + if (!cr_plat_setjmp(&coroutine_gdb_env)) + cr_plat_longjmp(env, 2); +} +#define cr_setjmp(env) ({ \ + int val = cr_plat_setjmp(env); \ + if (val == 2) { \ + cr_gdb_breakpoint(); \ + cr_plat_longjmp(&coroutine_gdb_env, 1); \ + } \ + val; \ + }) +#else +#define cr_setjmp(env) cr_plat_setjmp(env) +#endif +#define cr_longjmp(env) cr_plat_longjmp(env, 1) + static inline void assert_cid(cid_t cid) { assert(cid > 0); assert(cid <= CONFIG_COROUTINE_NUM); @@ -503,6 +546,11 @@ cid_t coroutine_add_with_stack_size(size_t stack_size, debugf("coroutine_add_with_stack_size(%zu, \"%s\", %p, %p)...", stack_size, name, fn, args); + if (!coroutine_initialized) { + cr_plat_init(); + coroutine_initialized = true; + } + cid_t child; { size_t base = last_created; @@ -524,8 +572,10 @@ cid_t coroutine_add_with_stack_size(size_t stack_size, memset(coroutine_table[child-1].name, 0, sizeof(coroutine_table[child-1].name)); coroutine_table[child-1].stack_size = stack_size; + infof("allocing \"%s\" stack with size %zu", name, stack_size); coroutine_table[child-1].stack = aligned_alloc(CR_PLAT_STACK_ALIGNMENT, stack_size); + infof("...done"); #if CONFIG_COROUTINE_MEASURE_STACK || CONFIG_COROUTINE_PROTECT_STACK for (size_t i = 0; i < stack_size; i++) ((uint8_t *)coroutine_table[child-1].stack)[i] = @@ -540,7 +590,7 @@ cid_t coroutine_add_with_stack_size(size_t stack_size, coroutine_running = child; coroutine_table[child-1].state = CR_INITIALIZING; coroutine_cnt++; - if (!cr_plat_setjmp(&coroutine_add_env)) { /* point=a */ + if (!cr_setjmp(&coroutine_add_env)) { /* point=a */ void *stack_base = coroutine_table[child-1].stack #if CR_PLAT_STACK_GROWS_DOWNWARD + stack_size @@ -577,6 +627,10 @@ cid_t coroutine_add(const char *name, cr_fn_t fn, void *args) { void coroutine_main(void) { debugf("coroutine_main()"); + if (!coroutine_initialized) { + cr_plat_init(); + coroutine_initialized = true; + } bool saved = cr_save_and_disable_interrupts(); assert(saved); assert(!cr_is_in_intrhandler()); @@ -589,10 +643,10 @@ void coroutine_main(void) { cr_plat_wait_for_interrupt(); } - if (!cr_plat_setjmp(&coroutine_main_env)) { /* point=b */ + if (!cr_setjmp(&coroutine_main_env)) { /* point=b */ coroutine_running = next; coroutine_table[coroutine_running-1].state = CR_RUNNING; - cr_plat_longjmp(&coroutine_table[coroutine_running-1].env, 1); /* jump to point=c */ + cr_longjmp(&coroutine_table[coroutine_running-1].env); /* jump to point=c */ } /* This is where we jump to from cr_exit(), and from * nowhere else. */ @@ -616,8 +670,8 @@ void cr_begin(void) { bool saved = cr_save_and_disable_interrupts(); coroutine_table[coroutine_running-1].state = CR_RUNNABLE; coroutine_ringbuf_push(coroutine_running); - if (!cr_plat_setjmp(&coroutine_table[coroutine_running-1].env)) /* point=c1 */ - cr_plat_longjmp(&coroutine_add_env, 1); /* jump to point=a */ + if (!cr_setjmp(&coroutine_table[coroutine_running-1].env)) /* point=c1 */ + cr_longjmp(&coroutine_add_env); /* jump to point=a */ cr_restore_interrupts(saved); } @@ -634,10 +688,10 @@ static inline void _cr_yield() { return; } - if (!cr_plat_setjmp(&coroutine_table[coroutine_running-1].env)) { /* point=c2 */ + if (!cr_setjmp(&coroutine_table[coroutine_running-1].env)) { /* point=c2 */ coroutine_running = next; coroutine_table[coroutine_running-1].state = CR_RUNNING; - cr_plat_longjmp(&coroutine_table[coroutine_running-1].env, 1); /* jump to point=c */ + cr_longjmp(&coroutine_table[coroutine_running-1].env); /* jump to point=c */ } } @@ -671,7 +725,7 @@ void cr_pause_and_yield(void) { (void)cr_save_and_disable_interrupts(); coroutine_table[coroutine_running-1].state = CR_NONE; - cr_plat_longjmp(&coroutine_main_env, 1); /* jump to point=b */ + cr_longjmp(&coroutine_main_env); /* jump to point=b */ } static void _cr_unpause(cid_t cid) { @@ -737,9 +791,9 @@ void cr_cid_info(cid_t cid, struct cr_cid_info *ret) { if (cid == coroutine_running) sp = cr_plat_get_sp(); else if (coroutine_table[cid-1].state == CR_RUNNING) - sp = coroutine_add_env.sp; + sp = cr_plat_setjmp_get_sp(&coroutine_add_env); else - sp = coroutine_table[cid-1].env.sp; + sp = cr_plat_setjmp_get_sp(&coroutine_table[cid-1].env); assert(sp); uintptr_t sb = (uintptr_t)coroutine_table[cid-1].stack; #if CR_PLAT_STACK_GROWS_DOWNWARD |