summaryrefslogtreecommitdiff
path: root/libcr/coroutine.c
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-12-08 08:28:16 -0700
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-12-08 08:28:16 -0700
commit96a751d8a5d20b2acea5ae8d10ac3d051466c2c3 (patch)
tree1ce291df34d78acd6f1619a779abbd4f06134e26 /libcr/coroutine.c
parentafe6542b30def82573e070371281c05dc593a739 (diff)
parentb9ebe41358244caa9334e72ca4e3c8c7a14c86e7 (diff)
Merge commit 'b9ebe41358244caa9334e72ca4e3c8c7a14c86e7'
Diffstat (limited to 'libcr/coroutine.c')
-rw-r--r--libcr/coroutine.c112
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