summaryrefslogtreecommitdiff
path: root/libcr_ipc
diff options
context:
space:
mode:
Diffstat (limited to 'libcr_ipc')
-rw-r--r--libcr_ipc/include/libcr_ipc/sema.h49
1 files changed, 36 insertions, 13 deletions
diff --git a/libcr_ipc/include/libcr_ipc/sema.h b/libcr_ipc/include/libcr_ipc/sema.h
index 257c8d5..9fe3223 100644
--- a/libcr_ipc/include/libcr_ipc/sema.h
+++ b/libcr_ipc/include/libcr_ipc/sema.h
@@ -10,6 +10,10 @@
#include <stdbool.h>
#include <libcr_ipc/_common.h>
+/* Most of the complexity in this file is so that
+ * cr_sema_signal_from_intrhandler() can actually be safe when called
+ * in an interrupt handler. */
+
/**
* A cr_sema_t is a fair unbounded[1] counting semaphore.
*
@@ -20,18 +24,20 @@ typedef struct {
struct _cr_ipc_cid_fifo waiters;
/* locked indicates that a call from within a coroutine is is
- * messing with ->waiters, so a signal handler can't read it.
- * Use `asm volatile ("":::"memory")` after setting =true and
- * before setting =false to make sure the compiler doesn't
- * re-order writes! */
+ * messing with ->waiters, so an interrupt handler can't read
+ * it. Use `asm volatile ("":::"memory")` after setting =true
+ * and before setting =false to make sure the compiler doesn't
+ * re-order writes to ->waiters to be outside of the critical
+ * section! */
volatile bool locked;
} cr_sema_t;
/**
* Drain a maximum of sema->cnt coroutines from the sema->waiters
- * list. Returns true if cr_getcid() was drained.
+ * list. Returns true if cr_getcid() was drained and we're not in an
+ * interrupt handler.
*/
-static inline bool _cr_sema_drain(cr_sema_t *sema) {
+static inline bool _cr_sema_drain(cr_sema_t *sema, bool in_intrhandler) {
assert(!sema->locked);
cid_t self = cr_getcid();
@@ -52,10 +58,14 @@ static inline bool _cr_sema_drain(cr_sema_t *sema) {
} else {
sema->cnt--;
cid_t cid = _cr_ipc_cid_fifo_pop(&sema->waiters);
- if (cid == self)
- state = DRAINED_SELF;
- else
- cr_unpause_from_sighandler(cid);
+ if (in_intrhandler)
+ cr_unpause_from_intrhandler(cid);
+ else {
+ if (cid == self)
+ state = DRAINED_SELF;
+ else
+ cr_unpause(cid);
+ }
}
}
asm volatile ("":::"memory");
@@ -75,12 +85,25 @@ static inline bool _cr_sema_drain(cr_sema_t *sema) {
*
* @blocks never
* @yields never
- * @run_in anywhere (coroutine, sighandler)
+ * @run_in coroutine
*/
static inline void cr_sema_signal(cr_sema_t *sema) {
sema->cnt++;
if (!sema->locked)
- _cr_sema_drain(sema);
+ assert(!_cr_sema_drain(sema, false));
+}
+
+/**
+ * Like cr_sema_signal(), but safe to call from an interrupt handler.
+ *
+ * @blocks never
+ * @yields never
+ * @run_in intrhandler
+ */
+static inline void cr_sema_signal_from_intrhandler(cr_sema_t *sema) {
+ sema->cnt++;
+ if (!sema->locked)
+ assert(!_cr_sema_drain(sema, true));
}
/**
@@ -99,7 +122,7 @@ static inline void cr_sema_wait(cr_sema_t *sema) {
asm volatile ("":::"memory");
sema->locked = false;
- if (_cr_sema_drain(sema))
+ if (_cr_sema_drain(sema, false))
/* DRAINED_SELF: (1) No need to pause+yield, (2) we
* better have been the last item in the list! */
assert(!self.next);