/* coroutine.h - Simple coroutine and request/response implementation * * Copyright (C) 2024 Luke T. Shumaker * SPDX-Licence-Identifier: AGPL-3.0-or-later */ #ifndef _COROUTINE_H_ #define _COROUTINE_H_ #include /* for size_t */ #include /* 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_ */