summaryrefslogtreecommitdiff
path: root/libcr/coroutine.c
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-11-15 00:46:44 -0700
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-11-19 20:15:48 -0700
commit5e04cdf350f9cede59263b52c2271f7c066439e3 (patch)
tree9d731c522a5a6d7b1bd49293f0350da1f4e2bd47 /libcr/coroutine.c
parent712f71f1a7c6d06ce9f8f011c5d5c03add0e9d72 (diff)
libcr: Begone with PRE_RUNNABLE
Diffstat (limited to 'libcr/coroutine.c')
-rw-r--r--libcr/coroutine.c75
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) {