8 months ago |
1 |
#ifndef COROUTINE_H | |||
2 |
#define COROUTINE_H | ||||
3 |
|||||
9 months ago |
2 |
4 |
#include <stdbool.h> | ||
7 months ago |
5 |
#include <stdint.h> | |||
9 months ago |
2 |
6 |
|||
8 months ago |
7 |
/////////////////////////////////////////////////////////////////////////////// | |||
8 |
// Coroutine | ||||
9 |
// | ||||
10 |
// Coroutines for C, based on setjmp/longjmp. | ||||
11 |
// Thread safe - each thread has its own coroutine system | ||||
12 |
// Coroutines are cooperatively scheduled | ||||
13 |
// Coroutines have their own stack (currently 16K each) | ||||
14 |
// A coroutine can be continued, queried, or deleted on a different thread. | ||||
15 |
// | ||||
16 |
// Usage: | ||||
17 |
// Coroutine_StartSystem(); // call once per thread before using coroutines | ||||
18 |
// Coroutine *co = Coroutine_New(start_function); | ||||
19 |
// void *result = Coroutine_Run(co, initial_value); | ||||
20 |
// Coroutine_Delete(co); | ||||
21 |
// Coroutine_StopSystem(); // call once per thread when done with coroutines | ||||
22 |
// | ||||
23 |
// Inside the coroutine function: | ||||
24 |
// void *value = Coroutine_Yield(yield_value, on_yield, this); | ||||
25 |
// ... | ||||
26 |
// return return_value; | ||||
27 |
// | ||||
28 |
// To create a coroutine: | ||||
29 |
// Coroutine *co = Coroutine_New(start_function); | ||||
30 |
// To start or continue a coroutine: | ||||
31 |
// void *result = Coroutine_Continue(co, value, early); | ||||
32 |
// // early=true puts the coroutine at the head of the run queue | ||||
33 |
// // early=false puts the coroutine at the tail of the run queue | ||||
34 |
// To yield from inside a coroutine: | ||||
35 |
// void *value = Coroutine_Yield(yield_value, on_yield, this); | ||||
36 |
// // on_yield is called before the next coroutine is run | ||||
37 |
// // 'this' is passed to on_yield as its parameter | ||||
38 |
// // value is the value passed to Coroutine_Continue | ||||
39 |
// To delete a coroutine: | ||||
40 |
// Coroutine_Delete(co); | ||||
41 |
// To get the value yielded from, or returned by a corotuine: | ||||
42 |
// void *value = Coroutine_GetValue(co); | ||||
43 |
// To get the currently running coroutine (NULL if none): | ||||
44 |
// Coroutine *co = Coroutine_GetActive(); | ||||
45 |
// To check if a coroutine is currently running: | ||||
46 |
// bool running = Coroutine_IsRunning(co); | ||||
47 |
// | ||||
48 |
// Notes: | ||||
49 |
// Coroutine is not expected to be used directly, but as a foundation for | ||||
50 |
// higher level constructs such as Generators, Async, etc. | ||||
51 |
// | ||||
52 |
/////////////////////////////////////////////////////////////////////////////// | ||||
53 |
|||||
54 |
|||||
55 |
// The stack is used as follows: | ||||
6 months ago |
56 |
// | |||
57 |
// Note: the stack is assumed to grow downwards through memory | ||||
58 |
// | ||||
59 |
// (low memory) | ||||
60 |
// | ||||
61 |
// <- limit. When set this and lower memory addresses are assumed to be unavailable for stack use | ||||
62 |
// (- lowest address usable by the stack -) | ||||
63 |
// . . | ||||
64 |
// . . | ||||
65 |
// | coroutine stack | | ||||
8 months ago |
66 |
// +------------------+ | |||
6 months ago |
67 |
// | coroutine header | <- 'tip' coroutine (latest allocated Coroutine) | |||
68 |
// +------------------+ | ||||
8 months ago |
69 |
// | coroutine stack | | |||
70 |
// +------------------+ | ||||
6 months ago |
71 |
// | coroutine header | <- 'active' Coroutine (the one currently running - could be 'tip') | |||
8 months ago |
72 |
// +------------------+ | |||
73 |
// | coroutine stack | | ||||
74 |
// +------------------+ | ||||
75 |
// | coroutine header | | ||||
76 |
// +------------------+ | ||||
77 |
// | coroutine stack | | ||||
78 |
// +------------------+ | ||||
79 |
// | coroutine header | | ||||
80 |
// +------------------+ | ||||
81 |
// | startup space | <- set aside by Coroutine_StartSystem | ||||
82 |
// +------------------+ | ||||
83 |
// | caller | <- This calls Coroutine_StartSystem etc | ||||
84 |
// +------------------+ | ||||
85 |
// | used stack | | ||||
6 months ago |
86 |
// +------------------+ <- stack 'bottom'; highest address used by the stack | |||
87 |
// (high memory) | ||||
8 months ago |
88 |
||||
89 |
// Each coroutine has this much stack: | ||||
8 months ago |
90 |
#ifndef COROUTINE_STACK_SIZE | |||
91 |
#define COROUTINE_STACK_SIZE 65536 | ||||
92 |
#endif | ||||
8 months ago |
93 |
||||
8 months ago |
94 |
// When Coroutine is started, an amount of stack is set aside to give | |||
8 months ago |
95 |
// the caller of Coroutine_StartSystem a bit of room to work before calling | |||
96 |
// Coroutine_Run(), that is this amount: | ||||
8 months ago |
97 |
#ifndef COROUTINE_STARTUP_STACK_SIZE | |||
98 |
#define COROUTINE_STARTUP_STACK_SIZE 4096 | ||||
99 |
#endif | ||||
8 months ago |
100 |
||||
6 months ago |
101 |
// When allocating space for a coroutine stack, fill it with guard pattern | |||
102 |
// so that lowest_headroom in the Coroutine_Report can be worked out | ||||
103 |
#ifndef COROUTINE_RECORD_LOWEST_HEADROOM | ||||
104 |
#define COROUTINE_RECORD_LOWEST_HEADROOM 1 | ||||
105 |
#endif | ||||
106 |
|||||
8 months ago |
107 |
// Returned by Coroutine_StopSystem(), this summarises the coroutine session | |||
108 |
typedef struct Coroutine_Report { | ||||
109 |
unsigned coroutines_created; | ||||
110 |
unsigned coroutines_pool_size; | ||||
111 |
unsigned lowest_headroom; | ||||
112 |
} Coroutine_Report; | ||||
113 |
|||||
9 months ago |
2 |
114 |
typedef struct Coroutine Coroutine; | ||
115 |
|||||
8 months ago |
116 |
typedef void (*Coroutine_YieldCallback)(void *me); | |||
9 months ago |
2 |
117 |
typedef void *(*Coroutine_Start)(void *); | ||
118 |
|||||
7 months ago |
119 |
extern void Coroutine_StartSystem(void); | |||
6 months ago |
120 |
extern void Coroutine_SetStackLimit(void *); | |||
7 months ago |
121 |
extern Coroutine_Report Coroutine_StopSystem(void); | |||
7 months ago |
122 |
extern Coroutine *Coroutine_New(Coroutine_Start start); | |||
123 |
extern void Coroutine_Run_Coroutine(Coroutine *cor, void *value); | ||||
124 |
extern void *Coroutine_Run(Coroutine_Start start, void *value); | ||||
125 |
extern void Coroutine_Delete(Coroutine *cor); | ||||
126 |
extern void Coroutine_Continue(Coroutine *cor, void *value, bool early); | ||||
127 |
extern void *Coroutine_Yield(void *value, Coroutine_YieldCallback on_yield, void *me); | ||||
128 |
extern void *Coroutine_GetValue(Coroutine *cor); | ||||
7 months ago |
129 |
extern Coroutine *Coroutine_GetActive(void); | |||
7 months ago |
130 |
extern intptr_t Coroutine_GetStackHeadroom(void); | |||
6 months ago |
131 |
extern void *Coroutine_GetStackHWM(void); | |||
132 |
extern void Coroutine_ClearStackForHWM(void); | ||||
6 months ago |
133 |
extern bool Coroutine_CanStartCoroutine(); | |||
7 months ago |
134 |
extern void *Coroutine_GetCStackTop(void); | |||
7 months ago |
135 |
extern void *Coroutine_Chain(Coroutine_Start start, void *value); | |||
7 months ago |
136 |
extern bool Coroutine_IsStarted(void); | |||
7 months ago |
137 |
extern bool Coroutine_IsRunning(Coroutine *cor); | |||
8 months ago |
138 |
||||
139 |
#endif | ||||
140 |