summaryrefslogtreecommitdiff
path: root/coroutine.h
blob: ae52f2208efbb93005ccf176d025f00935c02f6c (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
/* 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 */

/* typedefs *******************************************************************/

typedef size_t cid_t; /* 0=none; otherwise 1-indexed */
typedef void (*cr_fn_t)(void *args);

/* managing coroutines ********************************************************/

void coroutine_init(void);
cid_t coroutine_add(cr_fn_t fn, void *args);
void coroutine_main(void);

/* inside of coroutines *******************************************************/

bool _cr_begin(void);
#define cr_begin() do { if (!_cr_begin()) return; } while (0)
void cr_exit(void);
void cr_yield(void);
void cr_pause_and_yield(void);
void cr_unpause(cid_t);
#define cr_end()

cid_t cr_getcid(void);

/* 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 = cr_getcid();                          \
		(ch)->req = (_req);                                     \
		if ((ch)->responder != 0)                               \
			cr_unpause((ch)->responder);                    \
		cr_pause_and_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 = cr_getcid(); \
		if ((ch)->requester == 0) {    \
			cr_pause_and_yield();  \
		*(_req_p) = (ch)->req;         \
	} while (0)
#define cr_chan_send_resp(ch, _resp) do {    \
		cr_unpause((ch)->requester); \
		(ch)->responder = 0;         \
		(ch)->requester = 0;         \
		(ch)->resp = (_resp);        \
	} while (0)

#endif /* _COROUTINE_H_ */