diff options
Diffstat (limited to 'libcr')
-rw-r--r-- | libcr/coroutine.c | 60 |
1 files changed, 52 insertions, 8 deletions
diff --git a/libcr/coroutine.c b/libcr/coroutine.c index c446276..a947ae9 100644 --- a/libcr/coroutine.c +++ b/libcr/coroutine.c @@ -135,6 +135,8 @@ #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]] @@ -158,7 +160,10 @@ /* For a signal to be *in* the mask means that the signal is * *blocked*. */ - #define _CR_SIG_SENTINEL SIGHUP + #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; @@ -205,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; @@ -242,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 @@ -327,14 +346,7 @@ 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 @@ -415,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): @@ -471,7 +487,26 @@ 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) { @@ -511,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; @@ -587,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()); |