summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-11-17 22:08:54 -0700
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-11-19 20:24:20 -0700
commit91fefb09f03cc49966aee1a7c371f439d1834a4b (patch)
tree0551521c7e9cee17e09d363732704909a85ff0aa
parent58986ed5316acc6d6077541b7b6e754390069d73 (diff)
libcr: Add cr_is_in_intrhandler() and _cr_plat_are_interrupts_enabled()
-rw-r--r--libcr/coroutine.c68
-rw-r--r--libcr/include/libcr/coroutine.h6
2 files changed, 68 insertions, 6 deletions
diff --git a/libcr/coroutine.c b/libcr/coroutine.c
index 252eca2..041b624 100644
--- a/libcr/coroutine.c
+++ b/libcr/coroutine.c
@@ -161,7 +161,30 @@
/* 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) {
+ assert(!cr_is_in_intrhandler());
+ assert(!_cr_plat_are_interrupts_enabled());
sigset_t set;
sigemptyset(&set);
sigsuspend(&set);
@@ -170,29 +193,54 @@
sigprocmask(SIG_SETMASK, &set, NULL);
}
bool _cr_plat_save_and_disable_interrupts(void) {
+ assert(!cr_is_in_intrhandler());
sigset_t all, old;
sigfillset(&all);
sigprocmask(SIG_SETMASK, &all, &old);
- return !sigismember(&old, SIGRTMIN);
+ return !sigismember(&old, SIGHUP);
}
void _cr_plat_enable_interrupts(void) {
+ 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__
- static ALWAYS_INLINE void cr_plat_wait_for_interrupt(void) {
- asm volatile ("wfi":::"memory");
+ bool cr_is_in_intrhandler(void) {
+ uint32_t isr_number;
+ asm volatile ("mrs %0, ipsr"
+ : /* %0 */"=l"(isr_number)
+ );
+ return isr_number != 0;
}
- bool _cr_plat_save_and_disable_interrupts(void) {
+ static ALWAYS_INLINE bool _cr_plat_are_interrupts_enabled(void) {
+ assert(!cr_is_in_intrhandler());
uint32_t primask;
- asm volatile ("mrs %0, PRIMASK\n"
- "cpsid i"
+ asm volatile ("mrs %0, PRIMASK"
: /* %0 */"=l"(primask)
);
return primask == 0;
}
+
+ static ALWAYS_INLINE 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");
+ }
+ 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) {
+ assert(!cr_is_in_intrhandler());
+ assert(!_cr_plat_are_interrupts_enabled());
asm volatile ("cpsie i");
}
#else
@@ -498,6 +546,7 @@ __attribute__ ((noreturn)) void coroutine_main(void) {
debugf("coroutine_main()\n");
bool saved = cr_save_and_disable_interrupts();
assert(saved);
+ assert(!cr_is_in_intrhandler());
coroutine_running = 0;
for (;;) {
cid_t next;
@@ -561,6 +610,7 @@ static inline void _cr_yield() {
void cr_yield(void) {
debugf("cid=%zu: cr_yield()\n", coroutine_running);
+ assert(!cr_is_in_intrhandler());
assert_cid_state(coroutine_running, state == CR_RUNNING);
bool saved = cr_save_and_disable_interrupts();
@@ -572,6 +622,7 @@ void cr_yield(void) {
void cr_pause_and_yield(void) {
debugf("cid=%zu: cr_pause_and_yield()\n", coroutine_running);
+ assert(!cr_is_in_intrhandler());
assert_cid_state(coroutine_running, state == CR_RUNNING);
bool saved = cr_save_and_disable_interrupts();
@@ -582,6 +633,7 @@ void cr_pause_and_yield(void) {
__attribute__ ((noreturn)) void cr_exit(void) {
debugf("cid=%zu: cr_exit()\n", coroutine_running);
+ assert(!cr_is_in_intrhandler());
assert_cid_state(coroutine_running, state == CR_RUNNING);
(void)cr_save_and_disable_interrupts();
@@ -598,7 +650,9 @@ static void _cr_unpause(cid_t cid) {
void cr_unpause(cid_t cid) {
debugf("cr_unpause(%zu)\n", 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);
@@ -606,11 +660,13 @@ void cr_unpause(cid_t cid) {
void cr_unpause_from_intrhandler(cid_t cid) {
debugf("cr_unpause_from_intrhandler(%zu)\n", 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;
}
diff --git a/libcr/include/libcr/coroutine.h b/libcr/include/libcr/coroutine.h
index 8aeab7e..dc51a0f 100644
--- a/libcr/include/libcr/coroutine.h
+++ b/libcr/include/libcr/coroutine.h
@@ -153,6 +153,12 @@ bool _cr_plat_save_and_disable_interrupts(void);
void _cr_plat_enable_interrupts(void);
/**
+ * Return whether the current code is running in an interrupt handler
+ * or "normally".
+ */
+bool cr_is_in_intrhandler(void);
+
+/**
* cr_unpause_from_intrhandler() is like cr_unpause(), but safe to
* call from a interrupt handler.
*/