summaryrefslogtreecommitdiff
path: root/coroutine.h
diff options
context:
space:
mode:
Diffstat (limited to 'coroutine.h')
-rw-r--r--coroutine.h72
1 files changed, 72 insertions, 0 deletions
diff --git a/coroutine.h b/coroutine.h
new file mode 100644
index 0000000..77cb0a3
--- /dev/null
+++ b/coroutine.h
@@ -0,0 +1,72 @@
+/* coroutine.h - Simple coroutine and request/response implementation
+ *
+ * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-Licence-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _COROUTINE_H_
+#define _COROUTINE_H_
+
+#include <stddef.h> /* for size_t */
+#include <stdbool.h> /* for bool, true, false */
+
+/* typedefs *******************************************************************/
+typedef size_t cid_t; /* 0=none; otherwise 1-indexed */
+typedef void (*cr_fn_t)(void *stack);
+typedef struct {
+ cr_fn_t fn;
+ void *stack;
+ unsigned int state;
+ bool blocked;
+} _cr_entry_t;
+
+/* globals ********************************************************************/
+extern _cr_entry_t *_coroutine_table;
+extern cid_t _cur_cid;
+
+/* bona-fide functions ********************************************************/
+void coroutine_init(void);
+cid_t coroutine_add(cr_fn_t fn, void *stack);
+void coroutine_task(void);
+
+/* core macros for use in a cr_fn_t() *****************************************/
+#define cr_begin() switch(_coroutine_table[_cur_cid].state) { case 0:
+#define cr_exit() do { _coroutine_table[_cur_cid] = (cr_entry_t){0}; return; } while (0)
+#define cr_yield() _cr_yield(__COUNTER__)
+#define _cr_yield(_state) do { _coroutine_table[_cur_cid].state = _state; return; case _state:; } while (0)
+#define cr_end() }
+
+/* request/response channels **************************************************/
+#define cr_chan_t(req_t, resp_t) struct { \
+ cid_t requester; \
+ cid_t responder; \
+ req_t req; \
+ resp_t resp; \
+ }
+#define cr_chan_req(ch, _resp_p, _req) do { \
+ (ch)->requester = _cur_cid; \
+ (ch)->req = (_req); \
+ if ((ch)->responder != 0) \
+ _coroutine_table[(ch)->responder].blocked = false; \
+ _coroutine_table[_cur_cid].blocked = true; \
+ cr_yield(); \
+ if ((typeof(&(ch)->resp))(_resp_p)) \
+ *((typeof(&(ch)->resp))(_resp_p)) = (ch)->resp; \
+ } while (0)
+#define cr_chan_have_req(ch) ((ch)->requester != 0)
+#define cr_chan_recv_req(ch, _req_p) do { \
+ (ch)->responder = _cur_cid; \
+ if ((ch)->requester == 0) { \
+ _coroutine_table[_cur_cid].blocked = true; \
+ cr_yield(); \
+ } \
+ *(_req_p) = (ch)->req; \
+ } while (0)
+#define cr_chan_send_resp(ch, _resp) do { \
+ (ch)->responder = 0; \
+ (ch)->requester = 0; \
+ (ch)->resp = (_resp); \
+ _coroutine_table[(ch)->requester].blocked = false; \
+ } while (0)
+
+#endif /* _COROUTINE_H_ */