diff options
Diffstat (limited to 'coroutine.h')
-rw-r--r-- | coroutine.h | 72 |
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_ */ |