summaryrefslogtreecommitdiff
path: root/libcr/coroutine.c
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-11-21 21:19:44 -0700
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-11-23 13:56:51 -0700
commit60230d7862a9a87b4ef5531d0a549172335a2e3f (patch)
treee9355729fae6c6b00fc01188d3394b314f9c5edb /libcr/coroutine.c
parent0615af6f4748e4f7dc6a4f034a9db636742cc3bd (diff)
libcr: Group SP-tracking into a setjmp wrapper
Diffstat (limited to 'libcr/coroutine.c')
-rw-r--r--libcr/coroutine.c67
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