summaryrefslogtreecommitdiff
path: root/libcr_ipc/coroutine_chan.h
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-09-26 19:36:54 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-09-26 19:36:54 -0600
commit71e1a86a033c380f85dd300d788af63bfef25bab (patch)
tree07aa53d5a933ba51535a78972edbfe0cd95a31c5 /libcr_ipc/coroutine_chan.h
parentf5da707e77ee954b12f3c961012e4f40fa4e1bd3 (diff)
wip reorg
Diffstat (limited to 'libcr_ipc/coroutine_chan.h')
-rw-r--r--libcr_ipc/coroutine_chan.h115
1 files changed, 115 insertions, 0 deletions
diff --git a/libcr_ipc/coroutine_chan.h b/libcr_ipc/coroutine_chan.h
new file mode 100644
index 0000000..a3621a6
--- /dev/null
+++ b/libcr_ipc/coroutine_chan.h
@@ -0,0 +1,115 @@
+/* coroutine_chan.h - Simple channel system for coroutine.{h,c}
+ *
+ * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-Licence-Identifier: AGPL-3.0-or-later
+ */
+
+/**
+ * The cr_chan_* macros form a simple Go-like unbuffered channel
+ * mechanism built on top of the cr_pause_and_yeild() and cr_unpause()
+ * primitives.
+ */
+#ifndef _COROUTINE_CHAN_H_
+#define _COROUTINE_CHAN_H_
+
+#include <assert.h>
+
+#include "coroutine.h"
+
+/**
+ * cr_chan_t(val_t) returns the type definition for a channel on
+ * that transports values of type `val_t`.
+ */
+#define cr_chan_t(val_t) struct { \
+ bool ok_to_write; \
+ cid_t reader, *tail_reader; \
+ val_t *reader_val; \
+ \
+ bool ok_to_read; \
+ cid_t writer, *tail_writer; \
+ val_t *writer_val; \
+ }
+
+/* These "functions" are preprocessor macros instead of real C
+ * functions so that the compiler can do type-checking instead of
+ * having these functions take `void*`. */
+
+/**
+ * ch_chan_send(*ch, val) sends `val` over `ch`.
+ */
+#define cr_chan_send(ch, _val) do { \
+ if ((ch)->ok_to_write) { /* non-blocking */ \
+ *((ch)->reader_val) = (_val); \
+ (ch)->ok_to_write = false; \
+ cr_unpause((ch)->reader); \
+ } else { /* blocking */ \
+ cid_t next = 0; \
+ if ((ch)->writer) { \
+ *((ch)->tail_writer) = cr_getcid(); \
+ (ch)->tail_writer = &next; \
+ cr_pause_and_yield(); \
+ } else { \
+ (ch)->writer = cr_getcid(); \
+ (ch)->tail_writer = &next; \
+ } \
+ assert((ch)->writer == cr_getcid()); \
+ (ch)->writer_val = &(_val); \
+ (ch)->ok_to_read = true; \
+ cr_pause_and_yield(); \
+ assert((ch)->ok_to_read == false); \
+ if (next) { \
+ (ch)->writer = next; \
+ cr_unpause(next); \
+ } else { \
+ (ch)->writer = 0; \
+ (ch)->tail_writer = NULL; \
+ } \
+ } \
+ } while (0)
+
+/**
+ * cr_chan_recv(ch, val_p) reads a value from ch into `*val_p`.
+ */
+#define cr_chan_recv(ch, _val_p) do { \
+ if ((ch)->ok_to_read) { /* non-blocking */ \
+ *(_val_p) = *((ch)->writer_val); \
+ (ch)->ok_to_read = false; \
+ cr_unpause((ch)->writer; \
+ } else { /* blocking */ \
+ cid_t next = 0; \
+ if ((ch)->reader) { \
+ *((ch)->tail_reader) = cr_getcid(); \
+ (ch)->tail_reader = &next; \
+ cr_pause_and_yield(); \
+ } else { \
+ (ch)->reader = cr_getcid(); \
+ (ch)->tail_reader = &next; \
+ } \
+ assert((ch)->reader == cr_getcid()); \
+ (ch)->reader_val = (_val_p); \
+ (ch)->ok_to_write = true; \
+ cr_pause_and_yield(); \
+ assert((ch)->ok_to_write == false); \
+ if (next) { \
+ (ch)->reader = next; \
+ cr_unpause(next); \
+ } else { \
+ (ch)->reader = 0; \
+ (ch)->tail_reader = NULL; \
+ } \
+ } \
+ } while (0)
+
+/**
+ * cr_chan_can_send(ch) returns whether cr_chan_send(ch, val) would
+ * run without blocking.
+ */
+#define cr_chan_can_send(ch) ((ch)->ok_to_write)
+
+/**
+ * cr_chan_can_recv(ch) returns whether cr_chan_recv(ch, &val) would
+ * run without blocking.
+ */
+#define cr_chan_can_recv(ch) ((ch)->ok_to_read)
+
+#endif /* _COROUTINE_CHAN_H_ */