diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-11-15 00:46:44 -0700 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-11-19 20:15:48 -0700 |
commit | 5e04cdf350f9cede59263b52c2271f7c066439e3 (patch) | |
tree | 9d731c522a5a6d7b1bd49293f0350da1f4e2bd47 /libcr/coroutine.c | |
parent | 712f71f1a7c6d06ce9f8f011c5d5c03add0e9d72 (diff) |
libcr: Begone with PRE_RUNNABLE
Diffstat (limited to 'libcr/coroutine.c')
-rw-r--r-- | libcr/coroutine.c | 75 |
1 files changed, 36 insertions, 39 deletions
diff --git a/libcr/coroutine.c b/libcr/coroutine.c index 6549070..a468df0 100644 --- a/libcr/coroutine.c +++ b/libcr/coroutine.c @@ -155,7 +155,7 @@ /*==================================================================== * Interrupt management routines. */ #if __unix__ - #include <signal.h> /* for sig*, SIG_* */ + #include <signal.h> /* for sig*, SIG* */ /* For a signal to be *in* the mask means that the signal is * *blocked*. */ @@ -166,10 +166,11 @@ sigfillset(&set); sigprocmask(SIG_SETMASK, &set, NULL); } - void _cr_plat_disable_interrupts(void) { - sigset_t all; + bool _cr_plat_save_and_disable_interrupts(void) { + sigset_t all, old; sigfillset(&all); - sigprocmask(SIG_SETMASK, &all, NULL); + sigprocmask(SIG_SETMASK, &all, &old); + return !sigismember(&old, SIGRTMIN); } void _cr_plat_enable_interrupts(void) { sigset_t zero; @@ -181,8 +182,13 @@ static ALWAYS_INLINE void cr_plat_wait_for_interrupt(void) { asm volatile ("wfi":::"memory"); } - void _cr_plat_disable_interrupts(void) { - asm volatile ("cpsid i":::"memory"); + bool _cr_plat_save_and_disable_interrupts(void) { + uint32_t primask; + asm volatile ("mrs %0, PRIMASK\n" + "cpsid i" + : /* %0 */"=r"(primask) + ); + return primask == 0; } void _cr_plat_enable_interrupts(void) { asm volatile ("cpsie i":::"memory"); @@ -273,8 +279,6 @@ enum coroutine_state { CR_NONE = 0, /* this slot in the table is empty */ CR_INITIALIZING, /* running, before cr_begin() */ CR_RUNNING, /* running, after cr_begin() */ - CR_PRE_RUNNABLE, /* running, after cr_unpause_from_intrhandler() - * but before cr_pause() */ CR_RUNNABLE, /* not running, but runnable */ CR_PAUSED, /* not running, and not runnable */ }; @@ -301,7 +305,6 @@ const char *coroutine_state_strs[] = { [CR_NONE] = "CR_NONE", [CR_INITIALIZING] = "CR_INITIALIZING", [CR_RUNNING] = "CR_RUNNING", - [CR_PRE_RUNNABLE] = "CR_PRE_RUNNABLE", [CR_RUNNABLE] = "CR_RUNNABLE", [CR_PAUSED] = "CR_PAUSED", }; @@ -472,6 +475,11 @@ cid_t coroutine_add_with_stack_size(size_t stack_size, assert_cid_state(child, state == CR_RUNNABLE); if (parent) assert_cid_state(parent, state == CR_RUNNING); + /* Restore interrupts because cr_begin() disables interrupts + * before the context switch. XXX: This assumes that + * interrupts were enabled when _add() was called, which we + * didn't actually check. */ + cr_restore_interrupts(true); coroutine_running = parent; return child; @@ -484,9 +492,10 @@ cid_t coroutine_add(const char *name, cr_fn_t fn, void *args) { /* coroutine_main() ***********************************************************/ -void coroutine_main(void) { +__attribute__ ((noreturn)) void coroutine_main(void) { debugf("coroutine_main()\n"); - cr_disable_interrupts(); + bool saved = cr_save_and_disable_interrupts(); + assert(saved); coroutine_running = 0; for (;;) { cid_t next; @@ -518,12 +527,13 @@ void cr_begin(void) { debugf("cid=%zu: cr_begin()\n", coroutine_running); assert_cid_state(coroutine_running, state == CR_INITIALIZING); + 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 */ - cr_enable_interrupts(); + cr_restore_interrupts(saved); } static inline void _cr_yield() { @@ -551,37 +561,33 @@ void cr_yield(void) { debugf("cid=%zu: cr_yield()\n", coroutine_running); assert_cid_state(coroutine_running, state == CR_RUNNING); - cr_disable_interrupts(); + bool saved = cr_save_and_disable_interrupts(); coroutine_table[coroutine_running-1].state = CR_RUNNABLE; coroutine_ringbuf_push(coroutine_running); _cr_yield(); - cr_enable_interrupts(); + cr_restore_interrupts(saved); } void cr_pause_and_yield(void) { debugf("cid=%zu: cr_pause_and_yield()\n", coroutine_running); - assert_cid_state(coroutine_running, state == CR_RUNNING || state == CR_PRE_RUNNABLE); - - cr_disable_interrupts(); - if (coroutine_table[coroutine_running-1].state == CR_PRE_RUNNABLE) { - coroutine_table[coroutine_running-1].state = CR_RUNNABLE; - coroutine_ringbuf_push(coroutine_running); - } else - coroutine_table[coroutine_running-1].state = CR_PAUSED; + assert_cid_state(coroutine_running, state == CR_RUNNING); + + bool saved = cr_save_and_disable_interrupts(); + coroutine_table[coroutine_running-1].state = CR_PAUSED; _cr_yield(); - cr_enable_interrupts(); + cr_restore_interrupts(saved); } -void cr_exit(void) { +__attribute__ ((noreturn)) void cr_exit(void) { debugf("cid=%zu: cr_exit()\n", coroutine_running); assert_cid_state(coroutine_running, state == CR_RUNNING); - cr_disable_interrupts(); + (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 */ } -void cr_unpause(cid_t cid) { +void cr_unpause_from_intrhandler(cid_t cid) { debugf("cr_unpause(%zu)\n", cid); assert_cid_state(cid, state == CR_PAUSED); @@ -589,19 +595,10 @@ void cr_unpause(cid_t cid) { coroutine_ringbuf_push(cid); } -void cr_unpause_from_intrhandler(cid_t cid) { - debugf("cr_unpause_from_intrhandler(%zu)\n", cid); - assert_cid_state(cid, state == CR_RUNNING || state == CR_PAUSED); - - if (coroutine_table[cid-1].state == CR_RUNNING) { - assert(cid == coroutine_running); - debugf("... raced, deferring unpause\n"); - coroutine_table[cid-1].state = CR_PRE_RUNNABLE; - } else { - debugf("... actual unpause\n"); - coroutine_table[cid-1].state = CR_RUNNABLE; - coroutine_ringbuf_push(cid); - } +void cr_unpause(cid_t cid) { + bool saved = cr_save_and_disable_interrupts(); + cr_unpause_from_intrhandler(cid); + cr_restore_interrupts(saved); } cid_t cr_getcid(void) { |