diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-11-21 21:19:44 -0700 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-11-23 13:56:51 -0700 |
commit | 60230d7862a9a87b4ef5531d0a549172335a2e3f (patch) | |
tree | e9355729fae6c6b00fc01188d3394b314f9c5edb /libcr/coroutine.c | |
parent | 0615af6f4748e4f7dc6a4f034a9db636742cc3bd (diff) |
libcr: Group SP-tracking into a setjmp wrapper
Diffstat (limited to 'libcr/coroutine.c')
-rw-r--r-- | libcr/coroutine.c | 67 |
1 files changed, 40 insertions, 27 deletions
diff --git a/libcr/coroutine.c b/libcr/coroutine.c index c935c22..9cadc55 100644 --- a/libcr/coroutine.c +++ b/libcr/coroutine.c @@ -296,21 +296,42 @@ #endif /*==================================================================== - * Wrappers for setjmp()/longjmp() that do *not* save the - * interrupt mask. */ + * Wrappers for setjmp()/longjmp() that: + * 1. Allow us to inspect the buffer. + * 2. Do *not* save the interrupt mask. + */ + typedef struct { + jmp_buf raw; + #if CONFIG_COROUTINE_MEASURE_STACK + /* We aught to be able to get sp out of the raw + * `jmp_buf`, but libc authors insist on jmp_buf being + * opaque, glibc going as far as to xor it with a + * secret to obfuscate it! */ + uintptr_t sp; + #endif + } cr_plat_jmp_buf; + static inline void _cr_plat_setjmp_pre(cr_plat_jmp_buf *env) { + #if CONFIG_COROUTINE_MEASURE_STACK + env->sp = cr_plat_get_sp(); + #endif + } + /* cr_plat_setjmp *NEEDS* to be a preprocessor macro rather + * than a real function, because [[gnu::returns_twice]] + * doesn't work. + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117469 */ #if __unix__ /* On __unix__, we use POSIX real-time signals as interrupts. * POSIX leaves it implementation-defined whether * setjmp()/longjmp() save the signal mask; while glibc does * not save it, let's not rely on that. */ - #define cr_plat_setjmp(env) sigsetjmp(env, 0) - #define cr_plat_longjmp(env, val) siglongjmp(env, val) + #define cr_plat_setjmp(env) ({ _cr_plat_setjmp_pre(env); sigsetjmp((env)->raw, 0); }) + [[noreturn]] static void cr_plat_longjmp(cr_plat_jmp_buf *env, int val) { siglongjmp(env->raw, val); } #elif __NEWLIB__ /* newlib does not have sigsetjmp()/sigsetlongjmp(), but - * setjmp()/longjmp() do not save the interrupt mask, * so we + * setjmp()/longjmp() do not save the interrupt mask, so we * can use them directly. */ - #define cr_plat_setjmp(env) setjmp(env) - #define cr_plat_longjmp(env, val) longjmp(env, val) + #define cr_plat_setjmp(env) ({ _cr_plat_setjmp_pre(env); setjmp((env)->raw); }) + [[noreturn]] static void cr_plat_longjmp(cr_plat_jmp_buf *env, int val) { longjmp(env->raw, val); } #else #error unsupported platform (not __unix__, not __NEWLIB__) #endif @@ -338,13 +359,7 @@ enum coroutine_state { struct coroutine { volatile enum coroutine_state state; - jmp_buf env; -#if CONFIG_COROUTINE_MEASURE_STACK - /* We aught to be able to get this out of .env, but libc - * authors insist on jmp_buf being opaque, glibc going as far - * as to xor it with a secret ot obfuscate it! */ - uintptr_t sp; -#endif + cr_plat_jmp_buf env; size_t stack_size; void *stack; #if CONFIG_COROUTINE_VALGRIND @@ -379,8 +394,8 @@ static const uint8_t stack_pattern[] = { /* global variables ***********************************************************/ -static jmp_buf coroutine_add_env; -static jmp_buf coroutine_main_env; +static cr_plat_jmp_buf coroutine_add_env; +static cr_plat_jmp_buf coroutine_main_env; /* * Invariants (and non-invariants): @@ -502,7 +517,7 @@ cid_t coroutine_add_with_stack_size(size_t stack_size, coroutine_running = child; coroutine_table[child-1].state = CR_INITIALIZING; - if (!cr_plat_setjmp(coroutine_add_env)) { /* point=a */ + if (!cr_plat_setjmp(&coroutine_add_env)) { /* point=a */ void *stack_base = coroutine_table[child-1].stack #if CR_PLAT_STACK_GROWS_DOWNWARD + stack_size @@ -551,10 +566,10 @@ cid_t coroutine_add(const char *name, cr_fn_t fn, void *args) { cr_plat_wait_for_interrupt(); } - if (!cr_plat_setjmp(coroutine_main_env)) { /* point=b */ + if (!cr_plat_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_plat_longjmp(&coroutine_table[coroutine_running-1].env, 1); /* jump to point=c */ } /* This is where we jump to from cr_exit(), and from * nowhere else. */ @@ -576,9 +591,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); - coroutine_table[coroutine_running-1].sp = cr_plat_get_sp(); - 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_plat_setjmp(&coroutine_table[coroutine_running-1].env)) /* point=c1 */ + cr_plat_longjmp(&coroutine_add_env, 1); /* jump to point=a */ cr_restore_interrupts(saved); } @@ -595,11 +609,10 @@ static inline void _cr_yield() { return; } - coroutine_table[coroutine_running-1].sp = cr_plat_get_sp(); - if (!cr_plat_setjmp(coroutine_table[coroutine_running-1].env)) { /* point=c2 */ + if (!cr_plat_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_plat_longjmp(&coroutine_table[coroutine_running-1].env, 1); /* jump to point=c */ } } @@ -633,7 +646,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_plat_longjmp(&coroutine_main_env, 1); /* jump to point=b */ } static void _cr_unpause(cid_t cid) { @@ -699,7 +712,7 @@ void cr_cid_info(cid_t cid, struct cr_cid_info *ret) { if (cid == coroutine_running) sp = cr_plat_get_sp(); else - sp = coroutine_table[cid-1].sp; + sp = coroutine_table[cid-1].env.sp; assert(sp); uintptr_t sb = (uintptr_t)coroutine_table[cid-1].stack; #if CR_PLAT_STACK_GROWS_DOWNWARD |