summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-04-07 00:09:00 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-04-07 01:55:23 -0600
commit4d5a8b2f99be5e04954c5067080d1725af8c0ae7 (patch)
tree2b1d87719555eeb2159e7e79dcac5605dbb45c74
parent599c887d12d406296f356a02b1088f04ffe1f26e (diff)
libcr_ipc: Pull as much as possible from public .h to .c files
-rw-r--r--build-aux/measurestack/app_plugins.py4
-rw-r--r--cmd/sbc_harness/fs_harness_flash_bin.c2
-rw-r--r--lib9p/srv.c5
-rw-r--r--libcr_ipc/CMakeLists.txt6
-rw-r--r--libcr_ipc/_linkedlist.c62
-rw-r--r--libcr_ipc/_linkedlist.h51
-rw-r--r--libcr_ipc/chan.c141
-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.h74
-rw-r--r--libcr_ipc/include/libcr_ipc/mutex.h41
-rw-r--r--libcr_ipc/include/libcr_ipc/rpc.h86
-rw-r--r--libcr_ipc/include/libcr_ipc/select.h85
-rw-r--r--libcr_ipc/include/libcr_ipc/sema.h60
-rw-r--r--libcr_ipc/mutex.c45
-rw-r--r--libcr_ipc/rpc.c85
-rw-r--r--libcr_ipc/select.c102
-rw-r--r--libcr_ipc/sema.c66
-rw-r--r--libcr_ipc/tests/test_mutex.c2
-rw-r--r--libcr_ipc/tests/test_select.c2
-rw-r--r--libcr_ipc/tests/test_sema.c4
-rw-r--r--libhw_cr/rp2040_hwspi.c2
22 files changed, 565 insertions, 485 deletions
diff --git a/build-aux/measurestack/app_plugins.py b/build-aux/measurestack/app_plugins.py
index 36e661b..6eeb35b 100644
--- a/build-aux/measurestack/app_plugins.py
+++ b/build-aux/measurestack/app_plugins.py
@@ -247,8 +247,8 @@ class LibCRIPCPlugin:
return None
if "/chan.c:" in loc and "front->dequeue(" in line:
return [
- QName("_cr_chan_dequeue"),
- QName("_cr_select_dequeue"),
+ QName("cr_chan_dequeue"),
+ QName("cr_select_dequeue"),
], False
return None
diff --git a/cmd/sbc_harness/fs_harness_flash_bin.c b/cmd/sbc_harness/fs_harness_flash_bin.c
index bdb8da4..bcdf296 100644
--- a/cmd/sbc_harness/fs_harness_flash_bin.c
+++ b/cmd/sbc_harness/fs_harness_flash_bin.c
@@ -4,6 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
+#include <string.h>
+
#include <hardware/flash.h>
#include <hardware/watchdog.h>
diff --git a/lib9p/srv.c b/lib9p/srv.c
index a425dc9..60a1bb0 100644
--- a/lib9p/srv.c
+++ b/lib9p/srv.c
@@ -6,8 +6,10 @@
#include <alloca.h>
#include <inttypes.h> /* for PRI* */
-#include <stddef.h> /* for size_t */
#include <limits.h> /* for SSIZE_MAX, not set by newlib */
+#include <stddef.h> /* for size_t */
+#include <stdlib.h> /* for malloc() */
+#include <string.h> /* for memcpy() */
#ifndef SSIZE_MAX
#define SSIZE_MAX (SIZE_MAX >> 1)
#endif
@@ -15,7 +17,6 @@
#include <libcr/coroutine.h>
#include <libcr_ipc/chan.h>
#include <libcr_ipc/mutex.h>
-#include <libcr_ipc/select.h>
#include <libmisc/assert.h>
#include <libmisc/endian.h>
#include <libhw/generic/net.h>
diff --git a/libcr_ipc/CMakeLists.txt b/libcr_ipc/CMakeLists.txt
index cf8e5fd..7d249d7 100644
--- a/libcr_ipc/CMakeLists.txt
+++ b/libcr_ipc/CMakeLists.txt
@@ -6,11 +6,15 @@
add_library(libcr_ipc INTERFACE)
target_include_directories(libcr_ipc PUBLIC INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
target_sources(libcr_ipc INTERFACE
+ _linkedlist.c
chan.c
- select.c
+ mutex.c
+ rpc.c
+ sema.c
)
target_link_libraries(libcr_ipc INTERFACE
libcr
+ libmisc
)
set(ipc_tests
diff --git a/libcr_ipc/_linkedlist.c b/libcr_ipc/_linkedlist.c
new file mode 100644
index 0000000..b27dba8
--- /dev/null
+++ b/libcr_ipc/_linkedlist.c
@@ -0,0 +1,62 @@
+/* libcr_ipc/_linkedlist.c - 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
+ */
+
+#include <stddef.h> /* for NULL */
+
+#include "_linkedlist.h"
+
+/* singly linked list *********************************************************/
+
+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;
+}
+
+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 *********************************************************/
+
+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;
+}
+
+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;
+}
+
+void cr_ipc_dll_pop_from_front(_cr_ipc_dll_root *root) {
+ assert(root);
+ assert(root->front);
+ cr_ipc_dll_remove(root, root->front);
+}
diff --git a/libcr_ipc/_linkedlist.h b/libcr_ipc/_linkedlist.h
new file mode 100644
index 0000000..ab6d89e
--- /dev/null
+++ b/libcr_ipc/_linkedlist.h
@@ -0,0 +1,51 @@
+/* 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>
+
+#include <libcr_ipc/_linkedlist_pub.h>
+
+/* singly linked list *********************************************************/
+
+typedef struct _cr_ipc_sll_node {
+ struct _cr_ipc_sll_node *rear;
+} cr_ipc_sll_node;
+
+#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)); \
+ })
+
+void cr_ipc_sll_push_to_rear(_cr_ipc_sll_root *root, cr_ipc_sll_node *node);
+void cr_ipc_sll_pop_from_front(_cr_ipc_sll_root *root);
+
+/* doubly linked list *********************************************************/
+
+typedef struct _cr_ipc_dll_node {
+ struct _cr_ipc_dll_node *front, *rear;
+} cr_ipc_dll_node;
+
+#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)); \
+ })
+
+void cr_ipc_dll_push_to_rear(_cr_ipc_dll_root *root, cr_ipc_dll_node *node);
+void cr_ipc_dll_remove(_cr_ipc_dll_root *root, cr_ipc_dll_node *node);
+void cr_ipc_dll_pop_from_front(_cr_ipc_dll_root *root);
+
+#endif /* _LIBCR_IPC__LINKEDLIST_H_ */
diff --git a/libcr_ipc/chan.c b/libcr_ipc/chan.c
index 7dd1132..12d2ec2 100644
--- a/libcr_ipc/chan.c
+++ b/libcr_ipc/chan.c
@@ -4,11 +4,31 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
+#include <alloca.h> /* for alloca() */
+#include <string.h> /* for memcpy() */
+
+#include <libcr/coroutine.h> /* for cid_t, cr_* */
+#include <libmisc/assert.h>
+#include <libmisc/rand.h>
+
#include <libcr_ipc/chan.h>
-void _cr_chan_dequeue(void *_ch, size_t) {
+#include "_linkedlist.h"
+
+/* base channels **************************************************************/
+
+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;
+};
+
+void cr_chan_dequeue(void *_ch, size_t) {
struct _cr_chan *ch = _ch;
- _cr_ipc_dll_pop_from_front(&ch->waiters);
+ cr_ipc_dll_pop_from_front(&ch->waiters);
}
void _cr_chan_xfer(enum _cr_chan_waiter_typ self_typ, struct _cr_chan *ch, void *val_ptr, size_t val_size) {
@@ -17,7 +37,7 @@ void _cr_chan_xfer(enum _cr_chan_waiter_typ self_typ, struct _cr_chan *ch, void
if (ch->waiters.front && ch->waiter_typ != self_typ) { /* non-blocking fast-path */
/* Copy. */
- struct _cr_chan_waiter *front = _cr_ipc_dll_node_cast(struct _cr_chan_waiter, ch->waiters.front);
+ struct cr_chan_waiter *front = cr_ipc_dll_node_cast(struct cr_chan_waiter, ch->waiters.front);
if (self_typ == _CR_CHAN_SENDER)
memcpy(front->val_ptr, val_ptr, val_size);
else
@@ -27,14 +47,123 @@ void _cr_chan_xfer(enum _cr_chan_waiter_typ self_typ, struct _cr_chan *ch, void
front->dequeue_arg2);
cr_yield();
} else { /* blocking slow-path */
- struct _cr_chan_waiter self = {
+ struct cr_chan_waiter self = {
.cid = cr_getcid(),
.val_ptr = val_ptr,
- .dequeue = _cr_chan_dequeue,
+ .dequeue = cr_chan_dequeue,
.dequeue_arg1 = ch,
};
- _cr_ipc_dll_push_to_rear(&ch->waiters, &self);
+ cr_ipc_dll_push_to_rear(&ch->waiters, &self);
ch->waiter_typ = self_typ;
cr_pause_and_yield();
}
}
+
+/* select *********************************************************************/
+
+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;
+};
+
+static inline enum cr_select_class cr_select_getclass(struct cr_select_arg arg) {
+ switch (arg.op) {
+ case _CR_SELECT_OP_RECV:
+ if (arg.ch->waiters.front && arg.ch->waiter_typ == _CR_CHAN_SENDER)
+ return CR_SELECT_CLASS_NONBLOCK;
+ else
+ return CR_SELECT_CLASS_BLOCKING;
+ case _CR_SELECT_OP_SEND:
+ if (arg.ch->waiters.front && arg.ch->waiter_typ == _CR_CHAN_RECVER)
+ return CR_SELECT_CLASS_NONBLOCK;
+ else
+ return CR_SELECT_CLASS_BLOCKING;
+ case _CR_SELECT_OP_DEFAULT:
+ return CR_SELECT_CLASS_DEFAULT;
+ default:
+ assert_notreached("invalid arg.op");
+ }
+}
+
+void cr_select_dequeue(void *_waiters, size_t idx) {
+ struct cr_select_waiters *waiters = _waiters;
+ for (size_t i = 0; i < waiters->cnt; i++)
+ cr_ipc_dll_remove(&(waiters->args[i].ch->waiters),
+ &(waiters->nodes[i]));
+ waiters->cnt = idx;
+}
+
+size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]) {
+ size_t cnt_blocking = 0;
+ size_t cnt_nonblock = 0;
+ size_t cnt_default = 0;
+
+ assert(arg_cnt);
+ assert(arg_vec);
+ cr_assert_in_coroutine();
+
+ for (size_t i = 0; i < arg_cnt; i++) {
+ switch (cr_select_getclass(arg_vec[i])) {
+ case CR_SELECT_CLASS_BLOCKING:
+ cnt_blocking++;
+ break;
+ case CR_SELECT_CLASS_NONBLOCK:
+ cnt_nonblock++;
+ break;
+ case CR_SELECT_CLASS_DEFAULT:
+ cnt_default++;
+ break;
+ }
+ }
+
+ if (cnt_nonblock) {
+ size_t choice = rand_uint63n(cnt_nonblock);
+ for (size_t i = 0, seen = 0; i < arg_cnt; i++) {
+ if (cr_select_getclass(arg_vec[i]) == CR_SELECT_CLASS_NONBLOCK) {
+ if (seen == choice) {
+ _cr_chan_xfer(arg_vec[i].op == _CR_SELECT_OP_RECV
+ ? _CR_CHAN_RECVER
+ : _CR_CHAN_SENDER,
+ arg_vec[i].ch,
+ arg_vec[i].val_ptr,
+ arg_vec[i].val_siz);
+ return i;
+ }
+ seen++;
+ }
+ }
+ assert_notreached("should have returned from inside for() loop");
+ }
+
+ if (cnt_default) {
+ for (size_t i = 0; i < arg_cnt; i++)
+ if (cr_select_getclass(arg_vec[i]) == CR_SELECT_CLASS_DEFAULT)
+ return i;
+ assert_notreached("should have returned from inside for() loop");
+ }
+
+ struct cr_select_waiters waiters = {
+ .cnt = arg_cnt,
+ .args = arg_vec,
+ .nodes = alloca(sizeof(struct cr_chan_waiter) * arg_cnt),
+ };
+ for (size_t i = 0; i < arg_cnt; i++) {
+ waiters.nodes[i] = (struct cr_chan_waiter){
+ .cid = cr_getcid(),
+ .val_ptr = arg_vec[i].val_ptr,
+ .dequeue = cr_select_dequeue,
+ .dequeue_arg1 = &waiters,
+ .dequeue_arg2 = i,
+ };
+ cr_ipc_dll_push_to_rear(&arg_vec[i].ch->waiters, &waiters.nodes[i]);
+ }
+ cr_pause_and_yield();
+ return waiters.cnt;
+}
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 0e44d84..ad311a0 100644
--- a/libcr_ipc/include/libcr_ipc/chan.h
+++ b/libcr_ipc/include/libcr_ipc/chan.h
@@ -9,12 +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>
+#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
@@ -111,21 +111,65 @@ enum _cr_chan_waiter_typ {
_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/rpc.h b/libcr_ipc/include/libcr_ipc/rpc.h
index 512727b..07ace95 100644
--- a/libcr_ipc/include/libcr_ipc/rpc.h
+++ b/libcr_ipc/include/libcr_ipc/rpc.h
@@ -8,12 +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>
+#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
@@ -146,88 +144,12 @@ enum _cr_rpc_waiter_typ {
_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/select.h b/libcr_ipc/include/libcr_ipc/select.h
deleted file mode 100644
index f802348..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 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) **********************************************/
-
-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_ */
diff --git a/libcr_ipc/mutex.c b/libcr_ipc/mutex.c
new file mode 100644
index 0000000..28debba
--- /dev/null
+++ b/libcr_ipc/mutex.c
@@ -0,0 +1,45 @@
+/* libcr_ipc/mutex.c - Simple mutexes for libcr
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libcr/coroutine.h> /* for cid_t, cr_* */
+
+#define IMPLEMENTATION_FOR_LIBCR_IPC_MUTEX_H YES
+#include <libcr_ipc/mutex.h>
+
+#include "_linkedlist.h"
+
+struct cr_mutex_waiter {
+ cr_ipc_sll_node;
+ cid_t cid;
+};
+
+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_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;
+}
diff --git a/libcr_ipc/rpc.c b/libcr_ipc/rpc.c
new file mode 100644
index 0000000..a648fde
--- /dev/null
+++ b/libcr_ipc/rpc.c
@@ -0,0 +1,85 @@
+/* libcr_ipc/rpc.c - Simple request/response system for libcr
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <string.h> /* for memcpy() */
+
+#include <libcr/coroutine.h> /* for cid_t, cr_* */
+
+#include <libcr_ipc/rpc.h>
+
+#include "_linkedlist.h"
+
+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 */
+};
+
+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();
+ }
+}
+
+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;
+ }
+}
diff --git a/libcr_ipc/select.c b/libcr_ipc/select.c
deleted file mode 100644
index 4bd9067..0000000
--- a/libcr_ipc/select.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/* libcr_ipc/select.c - Select between channels
- *
- * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#include <libcr_ipc/select.h>
-
-static inline enum _cr_select_class _cr_select_getclass(struct cr_select_arg arg) {
- switch (arg.op) {
- case _CR_SELECT_OP_RECV:
- if (arg.ch->waiters.front && arg.ch->waiter_typ == _CR_CHAN_SENDER)
- return _CR_SELECT_CLASS_NONBLOCK;
- else
- return _CR_SELECT_CLASS_BLOCKING;
- case _CR_SELECT_OP_SEND:
- if (arg.ch->waiters.front && arg.ch->waiter_typ == _CR_CHAN_RECVER)
- return _CR_SELECT_CLASS_NONBLOCK;
- else
- return _CR_SELECT_CLASS_BLOCKING;
- case _CR_SELECT_OP_DEFAULT:
- return _CR_SELECT_CLASS_DEFAULT;
- default:
- assert_notreached("invalid arg.op");
- }
-}
-
-void _cr_select_dequeue(void *_waiters, size_t idx) {
- struct _cr_select_waiters *waiters = _waiters;
- for (size_t i = 0; i < waiters->cnt; i++)
- _cr_ipc_dll_remove(&(waiters->args[i].ch->waiters),
- &(waiters->nodes[i]));
- waiters->cnt = idx;
-}
-
-size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]) {
- size_t cnt_blocking = 0;
- size_t cnt_nonblock = 0;
- size_t cnt_default = 0;
-
- assert(arg_cnt);
- assert(arg_vec);
- cr_assert_in_coroutine();
-
- for (size_t i = 0; i < arg_cnt; i++) {
- switch (_cr_select_getclass(arg_vec[i])) {
- case _CR_SELECT_CLASS_BLOCKING:
- cnt_blocking++;
- break;
- case _CR_SELECT_CLASS_NONBLOCK:
- cnt_nonblock++;
- break;
- case _CR_SELECT_CLASS_DEFAULT:
- cnt_default++;
- break;
- }
- }
-
- if (cnt_nonblock) {
- size_t choice = rand_uint63n(cnt_nonblock);
- for (size_t i = 0, seen = 0; i < arg_cnt; i++) {
- if (_cr_select_getclass(arg_vec[i]) == _CR_SELECT_CLASS_NONBLOCK) {
- if (seen == choice) {
- _cr_chan_xfer(arg_vec[i].op == _CR_SELECT_OP_RECV
- ? _CR_CHAN_RECVER
- : _CR_CHAN_SENDER,
- arg_vec[i].ch,
- arg_vec[i].val_ptr,
- arg_vec[i].val_siz);
- return i;
- }
- seen++;
- }
- }
- assert_notreached("should have returned from inside for() loop");
- }
-
- if (cnt_default) {
- for (size_t i = 0; i < arg_cnt; i++)
- if (_cr_select_getclass(arg_vec[i]) == _CR_SELECT_CLASS_DEFAULT)
- return i;
- assert_notreached("should have returned from inside for() loop");
- }
-
- struct _cr_select_waiters waiters = {
- .cnt = arg_cnt,
- .args = arg_vec,
- .nodes = alloca(sizeof(struct _cr_chan_waiter) * arg_cnt),
- };
- for (size_t i = 0; i < arg_cnt; i++) {
- waiters.nodes[i] = (struct _cr_chan_waiter){
- .cid = cr_getcid(),
- .val_ptr = arg_vec[i].val_ptr,
- .dequeue = _cr_select_dequeue,
- .dequeue_arg1 = &waiters,
- .dequeue_arg2 = i,
- };
- _cr_ipc_dll_push_to_rear(&arg_vec[i].ch->waiters, &waiters.nodes[i]);
- }
- cr_pause_and_yield();
- return waiters.cnt;
-}
diff --git a/libcr_ipc/sema.c b/libcr_ipc/sema.c
new file mode 100644
index 0000000..85add6c
--- /dev/null
+++ b/libcr_ipc/sema.c
@@ -0,0 +1,66 @@
+/* libcr_ipc/sema.c - Simple semaphores for libcr
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libcr/coroutine.h> /* for cid_t, cr_* */
+
+#define IMPLEMENTATION_FOR_LIBCR_IPC_SEMA_H YES
+#include <libcr_ipc/sema.h>
+
+#include "_linkedlist.h"
+
+struct cr_sema_waiter {
+ cr_ipc_sll_node;
+ cid_t cid;
+};
+
+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_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_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);
+}
diff --git a/libcr_ipc/tests/test_mutex.c b/libcr_ipc/tests/test_mutex.c
index ee088f4..43714c9 100644
--- a/libcr_ipc/tests/test_mutex.c
+++ b/libcr_ipc/tests/test_mutex.c
@@ -28,7 +28,7 @@ COROUTINE cr_worker(void *_mu) {
}
int main() {
- cr_mutex_t mu = {0};
+ cr_mutex_t mu = {};
coroutine_add("a", cr_worker, &mu);
coroutine_add("b", cr_worker, &mu);
coroutine_main();
diff --git a/libcr_ipc/tests/test_select.c b/libcr_ipc/tests/test_select.c
index 9609534..1db645b 100644
--- a/libcr_ipc/tests/test_select.c
+++ b/libcr_ipc/tests/test_select.c
@@ -5,7 +5,7 @@
*/
#include <libcr/coroutine.h>
-#include <libcr_ipc/select.h>
+#include <libcr_ipc/chan.h>
#include "test.h"
diff --git a/libcr_ipc/tests/test_sema.c b/libcr_ipc/tests/test_sema.c
index 3208237..e5b22a5 100644
--- a/libcr_ipc/tests/test_sema.c
+++ b/libcr_ipc/tests/test_sema.c
@@ -5,6 +5,8 @@
*/
#include <libcr/coroutine.h>
+
+#define IMPLEMENTATION_FOR_LIBCR_IPC_SEMA_H YES /* so we can access .cnt */
#include <libcr_ipc/sema.h>
#include "test.h"
@@ -54,7 +56,7 @@ COROUTINE cr_consumer(void *_sema) {
}
int main() {
- cr_sema_t sema = {0};
+ cr_sema_t sema = {};
printf("== test 1 =========================================\n");
coroutine_add("first", cr_first, &sema);
diff --git a/libhw_cr/rp2040_hwspi.c b/libhw_cr/rp2040_hwspi.c
index 646d8ba..d181650 100644
--- a/libhw_cr/rp2040_hwspi.c
+++ b/libhw_cr/rp2040_hwspi.c
@@ -130,7 +130,7 @@ void _rp2040_hwspi_init(struct rp2040_hwspi *self,
self->dma_tx_data = dma3;
self->dma_rx_data = dma4;
self->dead_until_ns = 0;
- self->sema = (cr_sema_t){0};
+ self->sema = (cr_sema_t){};
/* Initialize the interrupt handler. */
/* We do this on (just) the rx channel, because the way the