summaryrefslogtreecommitdiff
path: root/libcr/coroutine.c
diff options
context:
space:
mode:
Diffstat (limited to 'libcr/coroutine.c')
-rw-r--r--libcr/coroutine.c366
1 files changed, 221 insertions, 145 deletions
diff --git a/libcr/coroutine.c b/libcr/coroutine.c
index 41d987e..726b732 100644
--- a/libcr/coroutine.c
+++ b/libcr/coroutine.c
@@ -4,13 +4,18 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <assert.h>
#include <setjmp.h> /* for setjmp(), longjmp(), jmp_buf */
#include <stdint.h> /* for uint8_t */
-#include <stdio.h> /* for printf(), fprintf(), stderr */
#include <stdlib.h> /* for aligned_alloc(), free() */
+#include <string.h> /* for strncpy(), memset() */
+
+#include <libmisc/assert.h>
+
+#define LOG_NAME COROUTINE
+#include <libmisc/log.h>
#include <libcr/coroutine.h>
+#undef COROUTINE
/* Configuration **************************************************************/
@@ -19,6 +24,9 @@
#ifndef CONFIG_COROUTINE_DEFAULT_STACK_SIZE
#error config.h must define CONFIG_COROUTINE_DEFAULT_STACK_SIZE
#endif
+#ifndef CONFIG_COROUTINE_NAME_LEN
+ #error config.h must define CONFIG_COROUTINE_NAME_LEN
+#endif
#ifndef CONFIG_COROUTINE_NUM
#error config.h must define CONFIG_COROUTINE_NUM
#endif
@@ -35,23 +43,24 @@
#error config.h must define CONFIG_COROUTINE_VALGRIND
#endif
+/* Implementation *************************************************************/
+
#if CONFIG_COROUTINE_VALGRIND
#include <valgrind/valgrind.h>
#endif
-/* Implementation *************************************************************/
-
/*
* Portability notes:
*
- * - It uses GCC `__attribute__`s, and the GNUC ({ ... }) statement
- * exprs extension.
+ * - It uses GCC `gnu::` attributes, and the GNUC `({ ... })`
+ * statement exprs extension.
*
* - It has a small bit of platform-specific code in the "platform
* support" section. Other than this, it should be portable to
* other platforms CPUs. It currently contains implementations for
- * __x86_64__ (assumes POSIX) and __arm__ (assumes bare-metal), and
- * should be fairly easy to add implementations for other platforms.
+ * __unix__ and __ARM_EABI__ "operating systems" on __x86_64__ and
+ * __ARM_ARCH_6M__ CPUs, and should be fairly easy to add
+ * implementations for other platforms.
*
* - It uses setjmp()/longjmp() in "unsafe" ways. POSIX-2017
* longjmp(3p) says
@@ -117,7 +126,7 @@
* no longer exists.
*/
-#define ALWAYS_INLINE inline __attribute__((always_inline))
+#define ALWAYS_INLINE [[gnu::always_inline]] inline
/* platform support ***********************************************************/
@@ -125,73 +134,113 @@
* able to run it on my x86-64 GNU/Linux laptop is useful for debugging. */
#define CR_PLAT_STACK_ALIGNMENT \
- ({ __attribute__((aligned)) void fn(void) {}; __alignof__(fn); })
+ ({ [[gnu::aligned]] void fn(void) {}; __alignof__(fn); })
#if 0
{ /* bracket to get Emacs indentation to work how I want */
#endif
/*====================================================================
- * Wrappers for setjmp()/longjmp() that do *not* save the
- * interrupt mask. */
-#if __unix__
- /* On a *NIX OS, we use 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)
-#elif __NEWLIB__
- /* newlib does not have sigsetjmp()/sigsetlongjmp(), but
- * 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)
-#else
- #error unsupported platform (not __unix__, not __NEWLIB__)
-#endif
-
-/*====================================================================
* Interrupt management routines. */
#if __unix__
- #include <signal.h> /* for sig*, SIG_* */
- #include <unistd.h> /* for pause() */
+ #include <signal.h> /* for sig*, SIG* */
+
+ /* For a signal to be *in* the mask means that the signal is
+ * *blocked*. */
+
+ bool cr_is_in_intrhandler(void) {
+ sigset_t cur_mask;
+ sigfillset(&cur_mask);
+ sigprocmask(0, NULL, &cur_mask);
+ if (sigismember(&cur_mask, SIGHUP))
+ /* Interrupts are disabled, so we cannot be in
+ * an interrupt handler. */
+ return false;
+ for (int sig = SIGRTMIN; sig <= SIGRTMAX; sig++)
+ if (sigismember(&cur_mask, sig))
+ return true;
+ return false;
+ }
+ static inline bool _cr_plat_are_interrupts_enabled(void) {
+ assert(!cr_is_in_intrhandler());
+ sigset_t cur_mask;
+ sigfillset(&cur_mask);
+ sigprocmask(0, NULL, &cur_mask);
+ return !sigismember(&cur_mask, SIGHUP);
+ }
static inline void cr_plat_wait_for_interrupt(void) {
- pause();
+ assert(!cr_is_in_intrhandler());
+ assert(!_cr_plat_are_interrupts_enabled());
+ sigset_t set;
+ sigemptyset(&set);
+ sigsuspend(&set);
+
+ sigfillset(&set);
+ sigprocmask(SIG_SETMASK, &set, NULL);
}
- void _cr_plat_disable_interrupts(void) {
- sigset_t all;
+ bool _cr_plat_save_and_disable_interrupts(void) {
+ assert(!cr_is_in_intrhandler());
+ sigset_t all, old;
sigfillset(&all);
- sigprocmask(SIG_BLOCK, &all, NULL);
+ sigprocmask(SIG_SETMASK, &all, &old);
+ return !sigismember(&old, SIGHUP);
}
void _cr_plat_enable_interrupts(void) {
- sigset_t all;
- sigfillset(&all);
- sigprocmask(SIG_UNBLOCK, &all, NULL);
+ assert(!cr_is_in_intrhandler());
+ assert(!_cr_plat_are_interrupts_enabled());
+ sigset_t zero;
+ sigemptyset(&zero);
+ sigprocmask(SIG_SETMASK, &zero, NULL);
+ }
+#elif __ARM_ARCH_6M__ && __ARM_EABI__
+ bool cr_is_in_intrhandler(void) {
+ uint32_t isr_number;
+ asm volatile ("mrs %0, ipsr"
+ : /* %0 */"=l"(isr_number)
+ );
+ return isr_number != 0;
+ }
+ ALWAYS_INLINE static bool _cr_plat_are_interrupts_enabled(void) {
+ assert(!cr_is_in_intrhandler());
+ uint32_t primask;
+ asm volatile ("mrs %0, PRIMASK"
+ : /* %0 */"=l"(primask)
+ );
+ return primask == 0;
}
-#elif __arm__
- /* Assume bare-metal if !__unix__. */
- static ALWAYS_INLINE void cr_plat_wait_for_interrupt(void) {
- asm volatile ("wfi":::"memory");
+
+ ALWAYS_INLINE static void cr_plat_wait_for_interrupt(void) {
+ assert(!cr_is_in_intrhandler());
+ assert(!_cr_plat_are_interrupts_enabled());
+ asm volatile ("wfi\n"
+ "cpsie i\n"
+ "isb\n"
+ "cpsid i"
+ :::"memory");
}
- void _cr_plat_disable_interrupts(void) {
- asm volatile ("cpsid i":::"memory");
+ bool _cr_plat_save_and_disable_interrupts(void) {
+ assert(!cr_is_in_intrhandler());
+ bool were_enabled = _cr_plat_are_interrupts_enabled();
+ asm volatile ("cpsid i");
+ return were_enabled;
}
void _cr_plat_enable_interrupts(void) {
- asm volatile ("cpsie i":::"memory");
+ assert(!cr_is_in_intrhandler());
+ assert(!_cr_plat_are_interrupts_enabled());
+ asm volatile ("cpsie i");
}
#else
- #error unsupported platform (not __unix__, not bare-metal __arm__)
+ #error unsupported platform (not __unix__, not __ARM_ARCH_6M__ && __ARM_EABI__)
#endif
/*====================================================================
* Stack management routines. */
-#if __arm__
+#if __ARM_ARCH_6M__
#define CR_PLAT_STACK_GROWS_DOWNWARD 1
#if CONFIG_COROUTINE_MEASURE_STACK
- static ALWAYS_INLINE uintptr_t cr_plat_get_sp(void) {
+ ALWAYS_INLINE static uintptr_t cr_plat_get_sp(void) {
uintptr_t sp;
asm volatile ("mov %0, sp":"=r"(sp));
return sp;
@@ -209,13 +258,13 @@
"mov sp, %1\n\t" /* [sp = stack] */
"mov r0, %3\n\t" /* [arg0 = args] */
"blx %2\n\t" /* [fn()] */
- "ldr r0, %0\n\t" /* [sp = staved_sp */
+ "ldr r0, %0\n\t" /* [sp = saved_sp */
"mov sp, r0" /* ] */
:
: /* %0 */"m"(saved_sp),
- /* %1 */"r"(stack),
- /* %2 */"r"(fn),
- /* %3 */"r"(args)
+ /* %1 */"l"(stack),
+ /* %2 */"l"(fn),
+ /* %3 */"l"(args)
: "r0"
);
}
@@ -223,7 +272,7 @@
#define CR_PLAT_STACK_GROWS_DOWNWARD 1
#if CONFIG_COROUTINE_MEASURE_STACK
- static ALWAYS_INLINE uintptr_t cr_plat_get_sp(void) {
+ ALWAYS_INLINE static uintptr_t cr_plat_get_sp(void) {
uintptr_t sp;
asm volatile ("movq %%rsp, %0":"=r"(sp));
return sp;
@@ -247,7 +296,48 @@
);
}
#else
- #error unsupported platform (not __arm__, not __x86__)
+ #error unsupported CPU (not __ARM_ARCH_6M__, not __x86_64__)
+#endif
+
+/*====================================================================
+ * 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) ({ _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
+ * can use them directly. */
+ #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
#if 0
@@ -267,26 +357,19 @@ 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 */
};
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
unsigned stack_id;
#endif
+ char name[CONFIG_COROUTINE_NAME_LEN];
};
/* constants ******************************************************************/
@@ -295,7 +378,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",
};
@@ -317,8 +399,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):
@@ -355,25 +437,6 @@ static cid_t coroutine_running = 0;
/* utility functions **********************************************************/
-#define errorf(...) fprintf(stderr, "error: " __VA_ARGS__)
-#define infof(...) printf("info: " __VA_ARGS__)
-#if CONFIG_COROUTINE_DEBUG
- #define debugf(...) printf("dbg: " __VA_ARGS__)
-#else
- #define debugf(...)
-#endif
-
-#ifdef __GLIBC__
- #define assertf(expr, ...) ({ \
- if (!(expr)) { \
- errorf("assertion: " __VA_ARGS__); \
- __assert_fail(#expr, __FILE__, __LINE__, __func__); \
- } \
- })
-#else
- #define assertf(expr, ...) assert(expr)
-#endif
-
static inline const char* coroutine_state_str(enum coroutine_state state) {
assert(state < ARRAY_LEN(coroutine_state_strs));
return coroutine_state_strs[state];
@@ -415,7 +478,9 @@ static inline void assert_cid(cid_t cid) {
/* coroutine_add() ************************************************************/
-cid_t coroutine_add_with_stack_size(size_t stack_size, cr_fn_t fn, void *args) {
+cid_t coroutine_add_with_stack_size(size_t stack_size,
+ const char *name,
+ cr_fn_t fn, void *args) {
static cid_t last_created = 0;
cid_t parent = coroutine_running;
@@ -423,8 +488,8 @@ cid_t coroutine_add_with_stack_size(size_t stack_size, cr_fn_t fn, void *args) {
assert_cid_state(parent, state == CR_RUNNING);
assert(stack_size);
assert(fn);
- debugf("coroutine_add_with_stack_size(%zu, %p, %p)...\n",
- stack_size, fn, args);
+ debugf("coroutine_add_with_stack_size(%zu, \"%s\", %p, %p)...",
+ stack_size, name, fn, args);
cid_t child;
{
@@ -437,10 +502,15 @@ cid_t coroutine_add_with_stack_size(size_t stack_size, cr_fn_t fn, void *args) {
return 0;
found:
}
- debugf("...child=%zu\n", child);
+ debugf("...child=%zu", child);
last_created = child;
+ if (name)
+ strncpy(coroutine_table[child-1].name, name, sizeof(coroutine_table[child-1].name));
+ else
+ memset(coroutine_table[child-1].name, 0, sizeof(coroutine_table[child-1].name));
+
coroutine_table[child-1].stack_size = stack_size;
coroutine_table[child-1].stack =
aligned_alloc(CR_PLAT_STACK_ALIGNMENT, stack_size);
@@ -457,7 +527,7 @@ cid_t coroutine_add_with_stack_size(size_t stack_size, cr_fn_t fn, void *args) {
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
@@ -466,45 +536,50 @@ cid_t coroutine_add_with_stack_size(size_t stack_size, cr_fn_t fn, void *args) {
+ STACK_GUARD_SIZE
#endif
;
- debugf("...stack =%p\n", coroutine_table[child-1].stack);
- debugf("...stack_base=%p\n", stack_base);
+ debugf("...stack =%p", coroutine_table[child-1].stack);
+ debugf("...stack_base=%p", stack_base);
/* run until cr_begin() */
cr_plat_call_with_stack(stack_base, fn, args);
- __builtin_unreachable(); /* should cr_begin() instead of returning */
+ assert_notreached("should cr_begin() instead of returning");
}
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;
}
-cid_t coroutine_add(cr_fn_t fn, void *args) {
+cid_t coroutine_add(const char *name, cr_fn_t fn, void *args) {
return coroutine_add_with_stack_size(
- CONFIG_COROUTINE_DEFAULT_STACK_SIZE, fn, args);
+ CONFIG_COROUTINE_DEFAULT_STACK_SIZE, name, fn, args);
}
/* coroutine_main() ***********************************************************/
-void coroutine_main(void) {
- debugf("coroutine_main()\n");
- cr_disable_interrupts();
+[[noreturn]] void coroutine_main(void) {
+ debugf("coroutine_main()");
+ bool saved = cr_save_and_disable_interrupts();
+ assert(saved);
+ assert(!cr_is_in_intrhandler());
coroutine_running = 0;
for (;;) {
cid_t next;
while ( !((next = coroutine_ringbuf_pop())) ) {
/* No coroutines are runnable, wait for an interrupt
* to change that. */
- cr_enable_interrupts();
cr_plat_wait_for_interrupt();
- cr_disable_interrupts();
}
- 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. */
@@ -520,15 +595,15 @@ void coroutine_main(void) {
/* cr_*() *********************************************************************/
void cr_begin(void) {
- debugf("cid=%zu: cr_begin()\n", coroutine_running);
+ debugf("cid=%zu: cr_begin()", 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();
+ 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);
}
static inline void _cr_yield() {
@@ -536,9 +611,7 @@ static inline void _cr_yield() {
while ( !((next = coroutine_ringbuf_pop())) ) {
/* No coroutines are runnable, wait for an interrupt
* to change that. */
- cr_enable_interrupts();
cr_plat_wait_for_interrupt();
- cr_disable_interrupts();
}
if (next == coroutine_running) {
@@ -546,72 +619,73 @@ 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 */
}
}
void cr_yield(void) {
- debugf("cid=%zu: cr_yield()\n", coroutine_running);
+ debugf("cid=%zu: cr_yield()", coroutine_running);
+ assert(!cr_is_in_intrhandler());
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;
+ debugf("cid=%zu: cr_pause_and_yield()", coroutine_running);
+ assert(!cr_is_in_intrhandler());
+ 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) {
- debugf("cid=%zu: cr_exit()\n", coroutine_running);
+[[noreturn]] void cr_exit(void) {
+ debugf("cid=%zu: cr_exit()", coroutine_running);
+ assert(!cr_is_in_intrhandler());
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 */
+ cr_plat_longjmp(&coroutine_main_env, 1); /* jump to point=b */
}
-void cr_unpause(cid_t cid) {
- debugf("cr_unpause(%zu)\n", cid);
+static void _cr_unpause(cid_t cid) {
assert_cid_state(cid, state == CR_PAUSED);
coroutine_table[cid-1].state = CR_RUNNABLE;
coroutine_ringbuf_push(cid);
}
+void cr_unpause(cid_t cid) {
+ debugf("cr_unpause(%zu)", cid);
+ assert(!cr_is_in_intrhandler());
+ assert_cid_state(coroutine_running, state == CR_RUNNING);
+
+ bool saved = cr_save_and_disable_interrupts();
+ _cr_unpause(cid);
+ cr_restore_interrupts(saved);
+}
+
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);
- }
+ debugf("cr_unpause_from_intrhandler(%zu)", cid);
+ assert(cr_is_in_intrhandler());
+
+ _cr_unpause(cid);
}
cid_t cr_getcid(void) {
+ assert(!cr_is_in_intrhandler());
+ assert_cid_state(coroutine_running, state == CR_RUNNING);
return coroutine_running;
}
@@ -647,8 +721,10 @@ void cr_cid_info(cid_t cid, struct cr_cid_info *ret) {
uintptr_t sp;
if (cid == coroutine_running)
sp = cr_plat_get_sp();
+ else if (coroutine_table[cid-1].state == CR_RUNNING)
+ sp = coroutine_add_env.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