summaryrefslogtreecommitdiff
path: root/coroutine.h
blob: d22eb2e24bb0936cd7aa2a239dac67a499eea111 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
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__+1)
#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_ */