summaryrefslogtreecommitdiff
path: root/libcr_ipc/include
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-04-07 20:29:35 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-04-07 20:29:35 -0600
commitd053a0f6dffc575611324b81acaef0d1554bda6c (patch)
tree3eb702a06625ef1f6be0648f7fec311b628808e9 /libcr_ipc/include
parent33b7cf675238367db2fc0528dea103d7792eaa0e (diff)
parent062e063f76c5937be4dc8465f6471ff9b5eb7be9 (diff)
Merge branch 'lukeshu/ipc'
Diffstat (limited to 'libcr_ipc/include')
-rw-r--r--libcr_ipc/include/libcr_ipc/_linkedlist.h99
-rw-r--r--libcr_ipc/include/libcr_ipc/_linkedlist_pub.h26
-rw-r--r--libcr_ipc/include/libcr_ipc/chan.h85
-rw-r--r--libcr_ipc/include/libcr_ipc/mutex.h41
-rw-r--r--libcr_ipc/include/libcr_ipc/owned_mutex.h79
-rw-r--r--libcr_ipc/include/libcr_ipc/rpc.h89
-rw-r--r--libcr_ipc/include/libcr_ipc/rwmutex.h77
-rw-r--r--libcr_ipc/include/libcr_ipc/select.h85
-rw-r--r--libcr_ipc/include/libcr_ipc/sema.h60
9 files changed, 190 insertions, 451 deletions
diff --git a/libcr_ipc/include/libcr_ipc/_linkedlist.h b/libcr_ipc/include/libcr_ipc/_linkedlist.h
deleted file mode 100644
index 543e058..0000000
--- a/libcr_ipc/include/libcr_ipc/_linkedlist.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/* libcr_ipc/_linkedlist.h - Common low-level linked lists for use in libcr_ipc
- *
- * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _LIBCR_IPC__LINKEDLIST_H_
-#define _LIBCR_IPC__LINKEDLIST_H_
-
-#include <libmisc/assert.h>
-
-/* singly linked list *********************************************************/
-
-typedef struct __cr_ipc_sll_node {
- struct __cr_ipc_sll_node *rear;
-} _cr_ipc_sll_node;
-
-typedef struct {
- _cr_ipc_sll_node *front, *rear;
-} _cr_ipc_sll_root;
-
-#define _cr_ipc_sll_node_cast(node_typ, node_ptr) \
- ({ \
- static_assert(_Generic(node_ptr, _cr_ipc_sll_node *: 1, default: 0), \
- "typeof("#node_ptr") != _cr_ipc_sll_node *"); \
- assert(node_ptr); \
- static_assert(offsetof(node_typ, _cr_ipc_sll_node) == 0); \
- ((node_typ*)(node_ptr)); \
- })
-
-static inline void _cr_ipc_sll_push_to_rear(_cr_ipc_sll_root *root, _cr_ipc_sll_node *node) {
- assert(root);
- node->rear = NULL;
- if (root->rear)
- root->rear->rear = node;
- else
- root->front = node;
- root->rear = node;
-}
-
-static inline void _cr_ipc_sll_pop_from_front(_cr_ipc_sll_root *root) {
- assert(root);
- assert(root->front);
- root->front = root->front->rear;
- if (!root->front)
- root->rear = NULL;
-}
-
-/* doubly linked list *********************************************************/
-
-typedef struct __cr_ipc_dll_node {
- struct __cr_ipc_dll_node *front, *rear;
-} _cr_ipc_dll_node;
-
-typedef struct {
- _cr_ipc_dll_node *front, *rear;
-} _cr_ipc_dll_root;
-
-#define _cr_ipc_dll_node_cast(node_typ, node_ptr) \
- ({ \
- static_assert(_Generic(node_ptr, _cr_ipc_dll_node *: 1, default: 0), \
- "typeof("#node_ptr") != _cr_ipc_dll_node *"); \
- assert(node_ptr); \
- static_assert(offsetof(node_typ, _cr_ipc_dll_node) == 0); \
- ((node_typ*)(node_ptr)); \
- })
-
-static inline void _cr_ipc_dll_push_to_rear(_cr_ipc_dll_root *root, _cr_ipc_dll_node *node) {
- assert(root);
- assert(node);
- node->front = root->rear;
- node->rear = NULL;
- if (root->rear)
- root->rear->rear = node;
- else
- root->front = node;
- root->rear = node;
-}
-
-static inline void _cr_ipc_dll_remove(_cr_ipc_dll_root *root, _cr_ipc_dll_node *node) {
- assert(root);
- assert(node);
- if (node->front)
- node->front->rear = node->rear;
- else
- root->front = node->rear;
- if (node->rear)
- node->rear->front = node->front;
- else
- root->rear = node->front;
-}
-
-static inline void _cr_ipc_dll_pop_from_front(_cr_ipc_dll_root *root) {
- assert(root);
- assert(root->front);
- _cr_ipc_dll_remove(root, root->front);
-}
-
-#endif /* _LIBCR_IPC__LINKEDLIST_H_ */
diff --git a/libcr_ipc/include/libcr_ipc/_linkedlist_pub.h b/libcr_ipc/include/libcr_ipc/_linkedlist_pub.h
new file mode 100644
index 0000000..6719ba4
--- /dev/null
+++ b/libcr_ipc/include/libcr_ipc/_linkedlist_pub.h
@@ -0,0 +1,26 @@
+/* libcr_ipc/_linkedlist_pub.h - Common low-level linked lists for use in libcr_ipc
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBCR_IPC__LINKEDLIST_PUB_H_
+#define _LIBCR_IPC__LINKEDLIST_PUB_H_
+
+/* singly linked list *********************************************************/
+
+struct _cr_ipc_sll_node;
+
+typedef struct {
+ struct _cr_ipc_sll_node *front, *rear;
+} _cr_ipc_sll_root;
+
+/* doubly linked list *********************************************************/
+
+struct _cr_ipc_dll_node;
+
+typedef struct {
+ struct _cr_ipc_dll_node *front, *rear;
+} _cr_ipc_dll_root;
+
+#endif /* _LIBCR_IPC__LINKEDLIST_PUB_H_ */
diff --git a/libcr_ipc/include/libcr_ipc/chan.h b/libcr_ipc/include/libcr_ipc/chan.h
index dafc92d..ad311a0 100644
--- a/libcr_ipc/include/libcr_ipc/chan.h
+++ b/libcr_ipc/include/libcr_ipc/chan.h
@@ -9,11 +9,12 @@
#include <stdbool.h> /* for bool */
#include <stddef.h> /* for size_t */
-#include <string.h> /* for memcpy */
-#include <libcr/coroutine.h> /* for cid_t, cr_* */
+#include <libmisc/macro.h> /* LM_CAT2_() */
-#include <libcr_ipc/_linkedlist.h>
+#include <libcr_ipc/_linkedlist_pub.h>
+
+/* base channels **************************************************************/
/**
* CR_CHAN_DECLARE(NAME, VAL_T) declares the following type and
@@ -78,51 +79,97 @@
struct _cr_chan core; \
VAL_T vals[0]; \
} NAME##_t; \
- \
+ \
static inline void NAME##_send(NAME##_t *ch, VAL_T val) { \
cr_assert_in_coroutine(); \
_cr_chan_xfer(_CR_CHAN_SENDER, &ch->core, &val, sizeof(val)); \
} \
- \
+ \
static inline VAL_T NAME##_recv(NAME##_t *ch) { \
cr_assert_in_coroutine(); \
VAL_T val; \
_cr_chan_xfer(_CR_CHAN_RECVER, &ch->core, &val, sizeof(val)); \
return val; \
} \
- \
+ \
static inline bool NAME##_can_send(NAME##_t *ch) { \
cr_assert_in_coroutine(); \
return ch->core.waiters.front && \
ch->core.waiter_typ == _CR_CHAN_RECVER; \
} \
- \
+ \
static inline bool NAME##_can_recv(NAME##_t *ch) { \
cr_assert_in_coroutine(); \
return ch->core.waiters.front && \
ch->core.waiter_typ == _CR_CHAN_SENDER; \
- }
+ } \
+ \
+ extern int LM_CAT2_(_CR_CHAN_FORCE_SEMICOLON_, __COUNTER__)
enum _cr_chan_waiter_typ {
_CR_CHAN_SENDER,
_CR_CHAN_RECVER,
};
-struct _cr_chan_waiter {
- _cr_ipc_dll_node;
- cid_t cid;
- void *val_ptr;
- void (*dequeue)(void *, size_t);
- void *dequeue_arg1;
- size_t dequeue_arg2;
-};
-
struct _cr_chan {
enum _cr_chan_waiter_typ waiter_typ;
- _cr_ipc_dll_root waiters;
+ _cr_ipc_dll_root waiters;
};
-void _cr_chan_dequeue(void *_ch, size_t);
void _cr_chan_xfer(enum _cr_chan_waiter_typ self_typ, struct _cr_chan *ch, void *val_ptr, size_t val_size);
+/* cr_select arguments ********************************************************/
+
+/**
+ * Do not populate cr_select_arg yourself; use the
+ * CR_SELECT_{RECV,SEND,DEFAULT} macros.
+ */
+struct cr_select_arg {
+ enum {
+ _CR_SELECT_OP_RECV,
+ _CR_SELECT_OP_SEND,
+ _CR_SELECT_OP_DEFAULT,
+ } op;
+ struct _cr_chan *ch;
+ void *val_ptr;
+ size_t val_siz;
+};
+
+#define CR_SELECT_RECV(CH, VALP) \
+ /* The _valp temporary variable is to get the compiler to check that \
+ * the types are compatible. */ \
+ ((struct cr_select_arg){ \
+ .op = _CR_SELECT_OP_RECV, \
+ .ch = &((CH)->core), \
+ .val_ptr = ({ typeof((CH)->vals[0]) *_valp = VALP; _valp; }), \
+ .val_siz = sizeof((CH)->vals[0]), \
+ })
+/* BUG: It's bogus that CR_SELECT_SEND takes VALP instead of VAL, but
+ * since we need an address, taking VAL would introduce uncomfortable
+ * questions about where VAL sits on the stack. */
+#define CR_SELECT_SEND(CH, VALP) \
+ /* The _valp temporary variable is to get the compiler to check that \
+ * the types are compatible. */ \
+ ((struct cr_select_arg){ \
+ .op = _CR_SELECT_OP_SEND, \
+ .ch = &((CH)->core), \
+ .val_ptr = ({ typeof((CH)->vals[0]) *_valp = VALP; _valp; }), \
+ .val_siz = sizeof((CH)->vals[0]), \
+ })
+#define CR_SELECT_DEFAULT \
+ ((struct cr_select_arg){ \
+ .op = _CR_SELECT_OP_DEFAULT, \
+ })
+
+/* cr_select_v(arg_cnt, arg_vec) **********************************************/
+
+size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]);
+
+/* cr_select_l(arg1, arg2, arg3, ...) ******************************************/
+
+#define cr_select_l(...) ({ \
+ struct cr_select_arg _cr_select_args[] = { __VA_ARGS__ }; \
+ cr_select_v(sizeof(_cr_select_args)/sizeof(_cr_select_args[0])); \
+})
+
#endif /* _LIBCR_IPC_CHAN_H_ */
diff --git a/libcr_ipc/include/libcr_ipc/mutex.h b/libcr_ipc/include/libcr_ipc/mutex.h
index cba8c19..ec40f5c 100644
--- a/libcr_ipc/include/libcr_ipc/mutex.h
+++ b/libcr_ipc/include/libcr_ipc/mutex.h
@@ -1,6 +1,6 @@
/* libcr_ipc/mutex.h - Simple mutexes for libcr
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
@@ -9,14 +9,9 @@
#include <stdbool.h> /* for bool */
-#include <libcr/coroutine.h> /* for cid_t, cr_* */
+#include <libmisc/private.h>
-#include <libcr_ipc/_linkedlist.h>
-
-struct _cr_mutex_waiter {
- _cr_ipc_sll_node;
- cid_t cid;
-};
+#include <libcr_ipc/_linkedlist_pub.h>
/**
* A cr_mutex_t is a fair mutex.
@@ -27,8 +22,10 @@ struct _cr_mutex_waiter {
* first place, then it has no business unlocking one.
*/
typedef struct {
+ BEGIN_PRIVATE(LIBCR_IPC_MUTEX_H);
bool locked;
_cr_ipc_sll_root waiters;
+ END_PRIVATE(LIBCR_IPC_MUTEX_H);
} cr_mutex_t;
/**
@@ -38,21 +35,7 @@ typedef struct {
* @cr_pauses maybe
* @cr_yields maybe
*/
-static inline void cr_mutex_lock(cr_mutex_t *mu) {
- assert(mu);
- cr_assert_in_coroutine();
-
- if (!mu->locked) /* non-blocking fast-path */
- mu->locked = true;
- else { /* blocking slow-path */
- struct _cr_mutex_waiter self = {
- .cid = cr_getcid(),
- };
- _cr_ipc_sll_push_to_rear(&mu->waiters, &self);
- cr_pause_and_yield();
- }
- assert(mu->locked);
-}
+void cr_mutex_lock(cr_mutex_t *mu);
/**
* Unlock the mutex. Unblocks a coroutine that is blocked on
@@ -62,16 +45,6 @@ static inline void cr_mutex_lock(cr_mutex_t *mu) {
* @cr_pauses never
* @cr_yields never
*/
-static inline void cr_mutex_unlock(cr_mutex_t *mu) {
- assert(mu);
- cr_assert_in_coroutine();
-
- assert(mu->locked);
- if (mu->waiters.front) {
- cr_unpause(_cr_ipc_sll_node_cast(struct _cr_mutex_waiter, mu->waiters.front)->cid);
- _cr_ipc_sll_pop_from_front(&mu->waiters);
- } else
- mu->locked = false;
-}
+void cr_mutex_unlock(cr_mutex_t *mu);
#endif /* _LIBCR_IPC_MUTEX_H_ */
diff --git a/libcr_ipc/include/libcr_ipc/owned_mutex.h b/libcr_ipc/include/libcr_ipc/owned_mutex.h
deleted file mode 100644
index c66240f..0000000
--- a/libcr_ipc/include/libcr_ipc/owned_mutex.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/* libcr_ipc/owned_mutex.h - Owned mutexes for libcr
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _LIBCR_IPC_OWNED_MUTEX_H_
-#define _LIBCR_IPC_OWNED_MUTEX_H_
-
-#include <stdbool.h> /* for bool */
-
-#include <libcr/coroutine.h> /* for cid_t, cr_unpause(), cr_pause_and_yield() */
-
-#include <libcr_ipc/_linkedlist.h>
-
-struct _cr_owned_mutex_waiter {
- _cr_ipc_sll_node;
- cid_t cid;
-};
-
-/**
- * A cr_owned_mutex_t is a fair mutex that tracks not just whether it
- * is locked, but which coroutine has it locked.
- *
- * None of the methods have `_from_intrhandler` variants because (1)
- * an interrupt handler can't block, so it shouldn't ever lock a mutex
- * because that can block; and (2) if it can't lock a mutex in the
- * first place, then it has no business unlocking one.
- */
-typedef struct {
- cid_t owner;
- _cr_ipc_sll_root waiters;
-} cr_owned_mutex_t;
-
-/**
- * Lock the mutex. Blocks if it is already locked.
- *
- * @runs_in coroutine
- * @cr_pauses maybe
- * @cr_yields maybe
- */
-static inline void cr_owned_mutex_lock(cr_owned_mutex_t *mu) {
- assert(mu);
-
- if (!mu->owner) /* non-blocking fast-path */
- mu->owner = cr_getcid();
- else { /* blocking slow-path */
- struct _cr_owned_mutex_waiter self = {
- .cid = cr_getcid(),
- };
- _cr_ipc_sll_push_to_rear(&mu->waiters, &self);
- cr_pause_and_yield();
- }
- assert(mu->owner == cr_getcid());
-}
-
-/**
- * Unlock the mutex. Unblocks a coroutine that is blocked on
- * cr_owned_mutex_lock(). Must be the called from the same coroutine
- * that called cr_owned_mutex_lock().
- *
- * @runs_in coroutine
- * @cr_pauses never
- * @cr_yields never
- */
-static inline void cr_owned_mutex_unlock(cr_owned_mutex_t *mu) {
- assert(mu);
-
- assert(mu->owner == cr_getcid());
- if (mu->waiters.front) {
- cid_t waiter = _cr_ipc_sll_node_cast(struct _cr_owned_mutex_waiter, mu->waiters.front)->cid;
- cr_unpause(waiter);
- mu->owner = waiter;
- _cr_ipc_sll_pop_from_front(&mu->waiters);
- } else
- mu->owner = 0;
-}
-
-#endif /* _LIBCR_IPC_OWNED_MUTEX_H_ */
diff --git a/libcr_ipc/include/libcr_ipc/rpc.h b/libcr_ipc/include/libcr_ipc/rpc.h
index 0600399..07ace95 100644
--- a/libcr_ipc/include/libcr_ipc/rpc.h
+++ b/libcr_ipc/include/libcr_ipc/rpc.h
@@ -8,11 +8,10 @@
#define _LIBCR_IPC_RPC_H_
#include <stdbool.h> /* for bool */
-#include <string.h> /* for memcpy() */
-#include <libcr/coroutine.h> /* for cid_t, cr_* */
+#include <libmisc/macro.h> /* for LM_CAT2_() */
-#include <libcr_ipc/_linkedlist.h>
+#include <libcr_ipc/_linkedlist_pub.h>
/**
* CR_RPC_DECLARE(NAME, REQ_T, RESP_T) declares the following types
@@ -136,95 +135,21 @@
*(req._resp) = resp; \
cr_unpause(req._requester); \
cr_yield(); \
- }
+ } \
+ \
+ extern int LM_CAT2_(_CR_RPC_FORCE_SEMICOLON_, __COUNTER__)
enum _cr_rpc_waiter_typ {
_CR_RPC_REQUESTER,
_CR_RPC_RESPONDER,
};
-struct _cr_rpc_requester {
- _cr_ipc_sll_node;
- cid_t cid;
- void *req_ptr; /* where to read req from */
- void *resp_ptr; /* where to write resp to */
-};
-
-struct _cr_rpc_responder {
- _cr_ipc_sll_node;
- /* before enqueued | after dequeued */
- /* -------------------+-------------------- */
- cid_t cid; /* responder cid | requester cid */
- void *ptr; /* where to write req | where to write resp */
-};
-
struct _cr_rpc {
enum _cr_rpc_waiter_typ waiter_typ;
_cr_ipc_sll_root waiters;
};
-static inline void _cr_rpc_send_req(struct _cr_rpc *ch,
- void *req_ptr, size_t req_size,
- void *resp_ptr)
-{
- assert(ch);
- assert(req_ptr);
- assert(resp_ptr);
-
- if (ch->waiters.front && ch->waiter_typ != _CR_RPC_REQUESTER) { /* fast-path (still blocks) */
- struct _cr_rpc_responder *responder =
- _cr_ipc_sll_node_cast(struct _cr_rpc_responder, ch->waiters.front);
- _cr_ipc_sll_pop_from_front(&ch->waiters);
- /* Copy the req to the responder's stack. */
- memcpy(responder->ptr, req_ptr, req_size);
- /* Notify the responder that we have done so. */
- cr_unpause(responder->cid);
- responder->cid = cr_getcid();
- responder->ptr = resp_ptr;
- /* Wait for the responder to set `*resp_ptr`. */
- cr_pause_and_yield();
- } else { /* blocking slow-path */
- struct _cr_rpc_requester self = {
- .cid = cr_getcid(),
- .req_ptr = req_ptr,
- .resp_ptr = resp_ptr,
- };
- _cr_ipc_sll_push_to_rear(&ch->waiters, &self);
- /* Wait for a responder to both copy our req and sed
- * `*resp_ptr`. */
- cr_pause_and_yield();
- }
-}
-
-static inline void _cr_rpc_recv_req(struct _cr_rpc *ch,
- void *req_ptr, size_t req_size,
- void **ret_resp_ptr,
- cid_t *ret_requester)
-{
- assert(ch);
- assert(req_ptr);
- assert(ret_resp_ptr);
- assert(ret_requester);
-
- if (ch->waiters.front && ch->waiter_typ != _CR_RPC_RESPONDER) { /* non-blocking fast-path */
- struct _cr_rpc_requester *requester =
- _cr_ipc_sll_node_cast(struct _cr_rpc_requester, ch->waiters.front);
- _cr_ipc_sll_pop_from_front(&ch->waiters);
-
- memcpy(req_ptr, requester->req_ptr, req_size);
- *ret_requester = requester->cid;
- *ret_resp_ptr = requester->resp_ptr;
- } else { /* blocking slow-path */
- struct _cr_rpc_responder self = {
- .cid = cr_getcid(),
- .ptr = req_ptr,
- };
- _cr_ipc_sll_push_to_rear(&ch->waiters, &self);
- ch->waiter_typ = _CR_RPC_RESPONDER;
- cr_pause_and_yield();
- *ret_requester = self.cid;
- *ret_resp_ptr = self.ptr;
- }
-}
+void _cr_rpc_send_req(struct _cr_rpc *ch, void *req_ptr, size_t req_size, void *resp_ptr);
+void _cr_rpc_recv_req(struct _cr_rpc *ch, void *req_ptr, size_t req_size, void **ret_resp_ptr, cid_t *ret_requester);
#endif /* _LIBCR_IPC_RPC_H_ */
diff --git a/libcr_ipc/include/libcr_ipc/rwmutex.h b/libcr_ipc/include/libcr_ipc/rwmutex.h
new file mode 100644
index 0000000..924acce
--- /dev/null
+++ b/libcr_ipc/include/libcr_ipc/rwmutex.h
@@ -0,0 +1,77 @@
+/* libcr_ipc/rwmutex.h - Simple read/write mutexes for libcr
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBCR_IPC_RWMUTEX_H_
+#define _LIBCR_IPC_RWMUTEX_H_
+
+#include <stdbool.h>
+
+#include <libmisc/private.h>
+
+#include <libcr_ipc/_linkedlist_pub.h>
+
+/**
+ * A cr_rwmutex_t is a fair read/write mutex.
+ *
+ * A waiting writer blocks any new readers; this ensures that the
+ * writer is able to eventually get the lock.
+ *
+ * None of the methods have `_from_intrhandler` variants because (1)
+ * an interrupt handler can't block, so it shouldn't ever lock a mutex
+ * because that can block; and (2) if it can't lock a mutex in the
+ * first place, then it has no business unlocking one.
+ */
+typedef struct {
+ BEGIN_PRIVATE(LIBCR_IPC_RWMUTEX_H);
+ unsigned nreaders;
+ bool locked;
+ bool unpausing;
+ _cr_ipc_sll_root waiters;
+ END_PRIVATE(LIBCR_IPC_RWMUTEX_H);
+} cr_rwmutex_t;
+
+/**
+ * Lock the mutex for writing. Blocks if it is already locked.
+ *
+ * @runs_in coroutine
+ * @cr_pauses maybe
+ * @cr_yields maybe
+ */
+void cr_rwmutex_lock(cr_rwmutex_t *mu);
+
+/**
+ * Undo a single cr_rwmutex_lock() call. Unblocks either a single
+ * coroutine that is blocked on cr_rwmutex_lock() or arbitrarily many
+ * coroutines that are blocked on cr_rwmutex_rlock().
+ *
+ * @runs_in coroutine
+ * @cr_pauses never
+ * @cr_yields never
+ */
+void cr_rwmutex_unlock(cr_rwmutex_t *mu);
+
+/**
+ * Lock the mutex for reading. Blocks if it is already locked for
+ * writing.
+ *
+ * @runs_in coroutine
+ * @cr_pauses maybe
+ * @cr_yields maybe
+ */
+void cr_rwmutex_rlock(cr_rwmutex_t *mu);
+
+/**
+ * Undo a single cr_rwmutext_rock() call. If the reader count is
+ * reduced to zero, unblocks a single coroutine that is blocked on
+ * cr_rwmutex_lock().
+ *
+ * @runs_in coroutine
+ * @cr_pauses never
+ * @cr_yields never
+ */
+void cr_rwmutex_runlock(cr_rwmutex_t *mu);
+
+#endif /* _LIBCR_IPC_RWMUTEX_H_ */
diff --git a/libcr_ipc/include/libcr_ipc/select.h b/libcr_ipc/include/libcr_ipc/select.h
deleted file mode 100644
index 0e35351..0000000
--- a/libcr_ipc/include/libcr_ipc/select.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/* libcr_ipc/select.h - Select between channels
- *
- * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#include <alloca.h> /* for alloca() */
-#include <stddef.h> /* for size_t */
-
-#include <libmisc/assert.h>
-#include <libmisc/rand.h>
-
-#include <libcr_ipc/chan.h>
-
-#ifndef _LIBCR_IPC_SELECT_H_
-#define _LIBCR_IPC_SELECT_H_
-
-/* arguments ******************************************************************/
-
-/**
- * Do not populate cr_select_arg yourself; use the
- * CR_SELECT_{RECV,SEND,DEFAULT} macros.
- */
-struct cr_select_arg {
- enum {
- _CR_SELECT_OP_RECV,
- _CR_SELECT_OP_SEND,
- _CR_SELECT_OP_DEFAULT,
- } op;
- struct _cr_chan *ch;
- void *val_ptr;
- size_t val_siz;
-};
-
-#define CR_SELECT_RECV(CH, VALP) ({ \
- /* The _valp indirection is to get the \
- * compiler to check that the types are \
- * compatible. */ \
- typeof((CH)->vals[0]) *_valp = VALP; \
- ((struct cr_select_arg){ \
- .op = _CR_SELECT_OP_RECV, \
- .ch = &((CH)->core), \
- .val_ptr = _valp, \
- .val_siz = sizeof((CH)->vals[0]), \
- }); \
- })
-#define CR_SELECT_SEND(CH, VAL) ({ \
- typeof((CH)->vals[0]) val_lvalue = VAL; \
- ((struct cr_select_arg){ \
- .op = _CR_SELECT_OP_SEND, \
- .ch = &((CH)->core), \
- .val_ptr = &val_lvalue;, \
- .val_siz = sizeof((CH)->vals[0]), \
- }); \
- })
-#define CR_SELECT_DEFAULT \
- ((struct cr_select_arg){ \
- .op = _CR_SELECT_OP_DEFAULT, \
- })
-
-/* cr_select_v(arg_cnt, arg_vec) **********************************************/
-
-enum _cr_select_class {
- _CR_SELECT_CLASS_DEFAULT,
- _CR_SELECT_CLASS_BLOCKING,
- _CR_SELECT_CLASS_NONBLOCK,
-};
-
-struct _cr_select_waiters {
- size_t cnt;
- struct cr_select_arg *args;
- struct _cr_chan_waiter *nodes;
-};
-
-void _cr_select_dequeue(void *_waiters, size_t idx);
-size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]);
-
-/* cr_select_l(arg1, arg2, arg3, ...) ******************************************/
-
-#define cr_select_l(...) ({ \
- struct cr_select_arg _cr_select_args[] = { __VA_ARGS__ }; \
- cr_select_v(sizeof(_cr_select_args)/sizeof(_cr_select_args[0])); \
-})
-
-#endif /* _LIBCR_IPC_SELECT_H_ */
diff --git a/libcr_ipc/include/libcr_ipc/sema.h b/libcr_ipc/include/libcr_ipc/sema.h
index fcabf28..ae8d93d 100644
--- a/libcr_ipc/include/libcr_ipc/sema.h
+++ b/libcr_ipc/include/libcr_ipc/sema.h
@@ -9,14 +9,9 @@
#include <stdbool.h>
-#include <libcr/coroutine.h> /* for cid_t, cr_* */
+#include <libmisc/private.h>
-#include <libcr_ipc/_linkedlist.h>
-
-struct _cr_sema_waiter {
- _cr_ipc_sll_node;
- cid_t cid;
-};
+#include <libcr_ipc/_linkedlist_pub.h>
/**
* A cr_sema_t is a fair unbounded[1] counting semaphore.
@@ -24,9 +19,11 @@ struct _cr_sema_waiter {
* [1]: Well, UINT_MAX
*/
typedef struct {
+ BEGIN_PRIVATE(LIBCR_IPC_SEMA_H);
unsigned int cnt;
bool unpausing;
_cr_ipc_sll_root waiters;
+ END_PRIVATE(LIBCR_IPC_SEMA_H);
} cr_sema_t;
/**
@@ -36,36 +33,14 @@ typedef struct {
* @cr_pauses never
* @cr_yields never
*/
-static inline void cr_sema_signal(cr_sema_t *sema) {
- assert(sema);
- cr_assert_in_coroutine();
-
- bool saved = cr_save_and_disable_interrupts();
- sema->cnt++;
- if (sema->waiters.front && !sema->unpausing) {
- cr_unpause(
- _cr_ipc_sll_node_cast(struct _cr_sema_waiter, sema->waiters.front)->cid);
- sema->unpausing = true;
- }
- cr_restore_interrupts(saved);
-}
+void cr_sema_signal(cr_sema_t *sema);
/**
* Like cr_sema_signal(), but for use from an interrupt handler.
*
* @runs_in intrhandler
*/
-static inline void cr_sema_signal_from_intrhandler(cr_sema_t *sema) {
- assert(sema);
- cr_assert_in_intrhandler();
-
- sema->cnt++;
- if (sema->waiters.front && !sema->unpausing) {
- cr_unpause_from_intrhandler(
- _cr_ipc_sll_node_cast(struct _cr_sema_waiter, sema->waiters.front)->cid);
- sema->unpausing = true;
- }
-}
+void cr_sema_signal_from_intrhandler(cr_sema_t *sema);
/**
* Wait until the semaphore is >0, then decrement it.
@@ -74,27 +49,6 @@ static inline void cr_sema_signal_from_intrhandler(cr_sema_t *sema) {
* @cr_pauses maybe
* @cr_yields maybe
*/
-static inline void cr_sema_wait(cr_sema_t *sema) {
- assert(sema);
- cr_assert_in_coroutine();
-
- bool saved = cr_save_and_disable_interrupts();
-
- struct _cr_sema_waiter self = {
- .cid = cr_getcid(),
- };
- _cr_ipc_sll_push_to_rear(&sema->waiters, &self);
- if (sema->waiters.front != &self._cr_ipc_sll_node || !sema->cnt)
- cr_pause_and_yield();
- assert(sema->waiters.front == &self._cr_ipc_sll_node && sema->cnt);
- _cr_ipc_sll_pop_from_front(&sema->waiters);
- sema->cnt--;
- if (sema->cnt && sema->waiters.front)
- cr_unpause(
- _cr_ipc_sll_node_cast(struct _cr_sema_waiter, sema->waiters.front)->cid);
- else
- sema->unpausing = false;
- cr_restore_interrupts(saved);
-}
+void cr_sema_wait(cr_sema_t *sema);
#endif /* _LIBCR_IPC_SEMA_H_ */