8 months ago |
1 |
Stackful coroutines in C. | |||
8 months ago |
17 |
2 |
|||
8 months ago |
3 |
* `Task` & `Future` coroutines which can pause, waiting for a future. | |||
4 |
* `ASleep` an example of pausing `Task`s using `Future`s. | ||||
8 months ago |
5 |
* `Generator` coroutines used as generators for loops. | |||
8 months ago |
6 |
* `Coroutine` the base coroutine engine. | |||
8 months ago |
17 |
7 |
|||
8 months ago |
8 |
Your code doesn't need to do anything special to be a coroutine. Only standard, or commonly available libraries are needed. | |||
8 months ago |
17 |
9 |
|||
8 months ago |
10 |
## Prerequisites | |||
8 months ago |
17 |
11 |
|||
8 months ago |
12 |
These libraries rely on as much as possible on C's cross-platform comfort zone. C's standard libraries are used as far as possible, but, as `threads.h` is not usually supported, `pthread.h` has been used instead. | |||
8 months ago |
17 |
13 |
|||
8 months ago |
14 |
You will need to build & link the code, `coroutine/*.c`, as part of your system, and ensure the headers, `include/*`, are available on your include path. | |||
8 months ago |
17 |
15 |
|||
7 months ago |
16 |
If your system doesn't have pthread, all the system-specific bits have been collected into cor_platform.c & .h. Replace these with versions which work with your platform. | |||
17 |
|||||
8 months ago |
18 |
## Quick Start | |||
8 months ago |
17 |
19 |
|||
8 months ago |
20 |
### Tasks | |||
8 months ago |
17 |
21 |
|||
8 months ago |
22 |
To run `Task`s: | |||
8 months ago |
17 |
23 |
|||
8 months ago |
24 |
#!C | |||
8 months ago |
25 |
#include "coroutine.h" | |||
26 |
#include "task.h" | ||||
8 months ago |
27 |
main(){ | |||
5 months ago |
28 |
size_t mytask_stack_size = 8192 * sizeof(void *); | |||
8 months ago |
29 |
void *res = NULL; | |||
5 months ago |
30 |
bool canceled = Task_Run(mytask_stack_size, maintask, ¶m, &res); | |||
8 months ago |
31 |
} | |||
32 |
|||||
8 months ago |
33 |
`Task_Run` runs tasks, switching between them when the current task waits on an `Future`. `maintask()` is run as a task. The start function for any task looks like this: | |||
8 months ago |
34 |
||||
8 months ago |
35 |
#!C | |||
8 months ago |
36 |
bool mytask(void *param, void **res){ | |||
37 |
|||||
38 |
// do your thing here | ||||
39 |
|||||
40 |
return canceled; | ||||
41 |
} | ||||
42 |
|||||
43 |
When `Task` returns from its start function, it returns whether it was canceled. Canceled `Task`s are assumed to have not finished what they were doing. | ||||
44 |
|||||
8 months ago |
45 |
Within your main task, create `Task`s and `Task_Await()` them when you want to wait for their result: | |||
8 months ago |
46 |
||||
8 months ago |
47 |
#!C | |||
8 months ago |
48 |
Task task1; | |||
5 months ago |
49 |
Task_ctor(&task1, mytask_stack_size, adifferenttask, &task1param); | |||
8 months ago |
50 |
||||
51 |
void *result; | ||||
8 months ago |
52 |
bool canceled = Task_Await(&task1, &result); | |||
8 months ago |
53 |
||||
8 months ago |
54 |
Task_dtor(&task1); | |||
8 months ago |
55 |
||||
56 |
// use the result | ||||
57 |
|||||
58 |
When a task needs to wait for something, and wants to allow other tasks to run, it should use a `Future`: | ||||
59 |
|||||
8 months ago |
60 |
#!C | |||
8 months ago |
61 |
Future future; | |||
62 |
Future_ctor(&future); | ||||
8 months ago |
63 |
||||
64 |
// pass the future to the background-thing-which-might-take-a-while | ||||
65 |
|||||
8 months ago |
66 |
void *res; | |||
8 months ago |
67 |
bool canceled = Future_Await(&future, &res); | |||
8 months ago |
68 |
||||
8 months ago |
69 |
Future_dtor(&future); | |||
8 months ago |
70 |
||||
71 |
When the background-thing-which-might-take-a-while has a result: | ||||
72 |
|||||
8 months ago |
73 |
#!C | |||
8 months ago |
74 |
Future_SetResult(future, false, result); | |||
8 months ago |
75 |
||||
8 months ago |
76 |
### ASleep | |||
77 |
|||||
78 |
`ASleep()` needs its own system to be started to work: | ||||
79 |
|||||
8 months ago |
80 |
#!C | |||
8 months ago |
81 |
ASleep_StartSystem() | |||
82 |
// Run tasks here which may now use ASLeep() | ||||
83 |
ASleep_StopSystem(); | ||||
84 |
|||||
3 months ago |
85 |
Note that `ASleep_StartSystem()` / `ASleep_StopSystem()` is only needed once per process. | |||
8 months ago |
86 |
||||
87 |
Sleeping in a task: | ||||
88 |
|||||
8 months ago |
89 |
#!C | |||
8 months ago |
90 |
bool mytask(void *param, void **result){ | |||
91 |
.. | ||||
92 |
ASleep(time_to_sleep); | ||||
93 |
.. | ||||
94 |
} | ||||
95 |
|||||
8 months ago |
96 |
### Generators | |||
97 |
|||||
3 months ago |
98 |
Your code needs to be in a `Coroutine` to use a `Generator`: | |||
8 months ago |
99 |
||||
8 months ago |
100 |
#!C | |||
3 months ago |
101 |
void *mycoroutine(void *param){ | |||
102 |
// You can use a Generator here | ||||
6 months ago |
103 |
} | |||
3 months ago |
104 |
||||
105 |
Coroutine_Run(StackSpace, mycoroutine, NULL, NULL); | ||||
6 months ago |
106 |
||||
8 months ago |
107 |
You will need a generator function: | |||
108 |
|||||
8 months ago |
109 |
#!C | |||
8 months ago |
110 |
void *yield_my_things(void *param){ | |||
111 |
bool domore = true; | ||||
112 |
|||||
113 |
// loop/call functions to find more values to yield, and when you have one: | ||||
114 |
domore = Generator_Yield(thing); | ||||
115 |
// .. if domore is false, exit your generator - it is being destructed | ||||
116 |
|||||
117 |
// not actually used by generators, but this is a useful convention for bubbling | ||||
118 |
// the flag out to calling functions. | ||||
119 |
return (void *)domore; | ||||
120 |
} | ||||
121 |
|||||
122 |
And to use it: | ||||
123 |
|||||
8 months ago |
124 |
#!C | |||
8 months ago |
125 |
Generator gen; | |||
5 months ago |
126 |
Generator_ctor(&gen, generator_stack_size, yield_my_things, ".."); | |||
8 months ago |
127 |
void *thing; | |||
128 |
while(Generator_Next(&gen, &thing)){ | ||||
129 |
// use thing - a value yielded by your generator | ||||
130 |
} | ||||
131 |
Generator_dtor(&gen); | ||||
132 |
|||||
133 |
### Coroutines | ||||
134 |
|||||
135 |
While you can use coroutines directly, it's designed as a system to support more useful patterns, like `Async` and `Generators`. | ||||
136 |
|||||
137 |
Your coroutine will need to have a start function: | ||||
138 |
|||||
8 months ago |
139 |
#!C | |||
8 months ago |
140 |
void *start(void *param){ | |||
141 |
... | ||||
142 |
} | ||||
143 |
|||||
144 |
When there is no coroutine running, start your 'main' coroutine: | ||||
145 |
|||||
8 months ago |
146 |
#!C | |||
5 months ago |
147 |
if (Coroutine_Run(coroutine_stack_size, comain, param, &result)){ | |||
5 months ago |
148 |
// handle the failure | |||
149 |
} | ||||
8 months ago |
150 |
||||
151 |
Create other coroutines like this: | ||||
152 |
|||||
8 months ago |
153 |
#!C | |||
8 months ago |
154 |
Coroutine *cor = Coroutine_New(start); | |||
155 |
|||||
3 months ago |
156 |
When you want a Coroutine to be queued to run: | |||
8 months ago |
157 |
||||
8 months ago |
158 |
#!C | |||
8 months ago |
159 |
Coroutine_Continue(cor, value, run_early); | |||
160 |
|||||
161 |
`value` will be start function's parameter, or the value returned from the yield. | ||||
162 |
|||||
3 months ago |
163 |
Within the Coroutine, to yield a value, and allow other `Coroutine`s to run: | |||
8 months ago |
164 |
||||
8 months ago |
165 |
#!C | |||
8 months ago |
166 |
void *Coroutine_Yield(value, on_yield, void *me); | |||
167 |
|||||
168 |
The on_yield function is called after the coroutine has been 'wait'ed, but before the next coroutine is resumed. | ||||
169 |
|||||
170 |
## How it Works | ||||
171 |
|||||
5 months ago |
172 |
The coroutine system uses the stack divided into smaller stacks for the coroutines. This means you may need to consider whether each coroutine's stack size, is right for each Coroutine, and whether your C stack size is enough for the number of coroutines you might run. | |||
8 months ago |
173 |
||||
8 months ago |
174 |
Each of your threads has its own stack - the coroutine system can be run (or not) independantly on each thread. For some special cases, you may want to adjust each of your thread's stack sizes depending on how it is used. | |||
8 months ago |
175 |
||||
176 |
## Style | ||||
177 |
|||||
8 months ago |
178 |
The style is influenced by C++. For example, where possible, a `Something *Something_New(a, b, c)` and `Something_Delete(Something *)` will have corresponding `Somthing_ctor(Somthing *, a, b, c)` and `Something_dtor(Something *)` to initialise and finalise a `Something` on the stack, or within another object. Using `.._ctor()` and `.._dtor()` will be faster as they avoid the `malloc()` and `free()`. | |||
8 months ago |
17 |
179 |
|||
8 months ago |
180 |
#!C | |||
8 months ago |
17 |
181 |
Something *oneofthem = Something_New(); | ||
182 |
// use oneofthem | ||||
183 |
Something_Delete(oneofthem); | ||||
184 |
|||||
185 |
Can be also be done like this, and this will run faster: | ||||
186 |
|||||
8 months ago |
187 |
#!C | |||
8 months ago |
17 |
188 |
Something oneofthem; | ||
189 |
Something_ctor(&oneofthem); | ||||
190 |
// use oneofthem | ||||
191 |
Something_dtor(&oneofthem); | ||||
192 |
|||||
8 months ago |
193 |
The exception is `Coroutine_New()` and `Coroutine_Delete()`. The returned `Coroutine` is somewhere on your thread's stack - its memory is managed by the coroutine system, and is allocated and freed quickly. | |||
194 |
|||||
8 months ago |
17 |
195 |
## Usage | ||
196 |
|||||
197 |
When you are using coroutines or generators: | ||||
198 |
|||||
8 months ago |
199 |
#!C | |||
8 months ago |
17 |
200 |
void *myfunc(void *){ | ||
201 |
// your function here | ||||
202 |
} | ||||
203 |
|||||
5 months ago |
204 |
size_t coroutine_stack_size = 8192 * sizeov(void *); | |||
205 |
if (Coroutine_Run(coroutine_stack_size, myfunc, (void *)myparam, NULL)){ | ||||
5 months ago |
206 |
// handle the failure | |||
207 |
} | ||||
8 months ago |
17 |
208 |
|||
5 months ago |
209 |
You can make many calls to `Coroutine_Run()` or `Task_Run()`. `Coroutine_Run()` ensures the system is started, and that `myfunc` is called | |||
210 |
from inside a Coroutine. In paeticular, if the Coroutine system is running and `Coroutine_Run()` is called from inside a coroutine, then `myfunc` is simply called. | ||||
8 months ago |
17 |
211 |
|||
8 months ago |
212 |
## Stack Overruns | |||
8 months ago |
17 |
213 |
|||
3 months ago |
214 |
The C stack is divided into smaller stacks. There's one, the startup stack, to give some room for `start` in `Coroutine_RunSystem` to work, and then each `Coroutine` has its own stack. These have guard markers which are checked to see if the stack has overrun. If there is a stack overrun, the system cannot continue - a message is output and the programe exited. There's a number of ways to avoid this issue: | |||
8 months ago |
17 |
215 |
|||
3 months ago |
216 |
* Use less stack. This is, sometimes, the right advice, especially if the startup stack overruns. The expectation is that very little is done by `start` in `Coroutine_RunSystem`. If your situation needs more doing, you can... | |||
8 months ago |
217 |
||||
3 months ago |
218 |
* increase the stack size for your `Coroutine`. If your use case is even more demanding, such as if you want 1000s of coroutines (so you need small stack chunks), /and/ some of them can recurse an unknown amount (so you need a deep stack for that `Coroutine`), then you can... | |||
8 months ago |
219 |
||||
220 |
* monitor stack headroom, and add another stack chunk if you need to: | ||||
221 |
|||||
222 |
In this last case you'll need to add some code at key points: | ||||
223 |
|||||
8 months ago |
224 |
#!C | |||
8 months ago |
225 |
void *myfunction(void *param){ | |||
226 |
if (Coroutine_GetStackHeadroom() < MIN_ALLOWED_STACK){ | ||||
5 months ago |
227 |
void *result; | |||
3 months ago |
228 |
Coroutine_Err err = Coroutine_Chain(my_stack_size, myfunction, param, &result); | |||
229 |
if (err){ | ||||
5 months ago |
230 |
// handle failure | |||
231 |
} | ||||
232 |
return result; | ||||
8 months ago |
233 |
} | |||
234 |
// do everything normally | ||||
235 |
} | ||||
236 |
|||||
237 |
More realistically: | ||||
238 |
|||||
8 months ago |
239 |
#!C | |||
8 months ago |
240 |
struct myfunctionparams { | |||
241 |
int a; | ||||
242 |
char *b; | ||||
243 |
struct dog *d; | ||||
244 |
} | ||||
245 |
|||||
246 |
void *mychain(void *param){ | ||||
247 |
struct myfunctionparams *myparams = (struct myfunctionparams *)params; | ||||
248 |
return (void *)myfunction(myparams->a, myparams->b, *myparams->d); | ||||
249 |
} | ||||
250 |
|||||
251 |
int myfunction(int a, char *b, struct dog d){ | ||||
252 |
if (Coroutine_GetStackHeadroom() < MIN_ALLOWED_STACK){ | ||||
253 |
struct myfunctionparams params = { | ||||
254 |
a, | ||||
255 |
b, | ||||
256 |
&d | ||||
257 |
}; | ||||
5 months ago |
258 |
void *result; | |||
3 months ago |
259 |
Coroutine_Err err = Coroutine_Chain(my_stack_size, mychain, ¶ms, &result); | |||
260 |
if (err){ | ||||
5 months ago |
261 |
// handle failure | |||
262 |
} | ||||
263 |
return (int)(intptr_t)result; | ||||
8 months ago |
264 |
} | |||
265 |
} | ||||
266 |
|||||
8 months ago |
267 |
And if you want to panic if the C stack overruns: | |||
268 |
|||||
8 months ago |
269 |
#!C | |||
8 months ago |
270 |
if (Coroutine_GetStackHeadroom() < MIN_ALLOWED_COROUTINE_STACK){ | |||
271 |
if (Coroutine_HasCoroutinesInFreePool() || | ||||
272 |
(char *)Coroutine_GetCStackTop() - c_stack_end >= MIN_ALLOWED_C_STACK) { | ||||
273 |
struct myfunctionparams params = { | ||||
274 |
a, | ||||
275 |
b, | ||||
276 |
&d | ||||
277 |
}; | ||||
5 months ago |
278 |
void *result; | |||
5 months ago |
279 |
if (Coroutine_Chain(my_stack_size, mychain, ¶ms, &result)){ | |||
5 months ago |
280 |
// handle failure | |||
281 |
} | ||||
282 |
return (int)(intptr_t)result; | ||||
8 months ago |
283 |
} | |||
284 |
// panic now | ||||
285 |
} | ||||
286 |
|||||
5 months ago |
287 |
## Configuring for Your Use Case | |||
288 |
|||||
2 days ago |
289 |
There's a number of adjustments which you may need to make for your situation. These are, mostly, in `cor_platform.h` and `cor_platform_inc.h`. | |||
5 months ago |
290 |
||||
6 days ago |
291 |
There's some options in `coroutine.h` which you may need to adjust: | |||
5 months ago |
292 |
||||
293 |
COROUTINE_STARTUP_STACK_SIZE | ||||
3 months ago |
294 |
: The amount of stack set aside for `start` in `Coroutine_RunSystem()`. | |||
5 months ago |
295 |
||||
296 |
COROUTINE_MINIMUM_STACK_SIZE | ||||
297 |
: The minimum stack size you'll ask for. The C stack is managed as a heap. If one of the free blocks in that | ||||
3 months ago |
298 |
heap is big enough for your new Coroutine, and has spare, if that spare is too small for a `Coroutine` wanting | |||
5 months ago |
299 |
`COROUTINE_MINIMUM_STACK_SIZE` of stack, then the whole free block is given to your new Coroutine, instead | |||
300 |
of being split into two. | ||||
301 |
|||||
2 days ago |
302 |
`cor_platform.h` has customisations for your particular use case. | |||
5 months ago |
303 |
||||
304 |
_Cor_thread_local | ||||
305 |
: How to declare a variable to be thread local | ||||
306 |
|||||
307 |
COROUTINE_HAVE_ALLOCA_H | ||||
308 |
: Whether your system can `#incude <alloca.h>`. | ||||
309 |
|||||
310 |
_Cor_Mutex and related routines | ||||
311 |
: Your system's mutex. | ||||
312 |
|||||
313 |
_Cor_Realtime_Now | ||||
314 |
: Return a realtime clock value compatible with _Cor_Semaphore_Wait. | ||||
315 |
|||||
316 |
_Cor_Semaphore and related routines | ||||
317 |
: Semaphores on your system. | ||||
318 |
|||||
319 |
_Cor_Thread and related routines | ||||
320 |
: Threads on your system. | ||||
321 |
|||||
2 days ago |
322 |
`cor_platform_inc.h` has more customisation for your particular use case, for coroutine.h to use. | |||
323 |
|||||
324 |
Coroutine_NS(name) | ||||
325 |
: This allows the `Coroutine_???()` functions to be given a different naming convention. | ||||
326 |
|||||
327 |
Coroutine_API_FUNC(TYPE) | ||||
328 |
: This allows tagging of functions to be accessible to loaded dlls. | ||||
329 |
|||||
8 months ago |
330 |
# API | |||
8 months ago |
17 |
331 |
|||
8 months ago |
332 |
## Task & Future | |||
8 months ago |
333 |
||||
334 |
The pattern for using async is: | ||||
335 |
|||||
8 months ago |
336 |
#!C | |||
8 months ago |
337 |
bool mymaintask(void *param, void **result){ | |||
338 |
// do your main task things here, like starting more tasks | ||||
8 months ago |
339 |
} | |||
340 |
|||||
341 |
void *res = NULL; | ||||
8 months ago |
342 |
bool canceled = Task_Run(mymaintask, NULL, &res); | |||
8 months ago |
343 |
||||
8 months ago |
344 |
To create and wait for a task: | |||
8 months ago |
345 |
||||
8 months ago |
346 |
#!C | |||
8 months ago |
347 |
Task task1; | |||
348 |
Task_ctor(&task1, asynctask1, &task1param); | ||||
8 months ago |
349 |
void *res = NULL; | |||
8 months ago |
350 |
bool canceled = Task_Await(&task1, void **res) | |||
351 |
Task_dtor(&task1); | ||||
8 months ago |
352 |
||||
353 |
or, if you prefer new & delete: | ||||
354 |
|||||
8 months ago |
355 |
#!C | |||
8 months ago |
356 |
Task *task1 = Task_New(asynctask1, &task1param); | |||
8 months ago |
357 |
void *res = NULL; | |||
8 months ago |
358 |
bool canceled = Task_Await(task1, void **res) | |||
359 |
Task_Delete(task1); | ||||
8 months ago |
360 |
||||
361 |
Inside your task, when there is something to wait for and you want other tasks to run while your task is waiting, you will need a future: | ||||
362 |
|||||
8 months ago |
363 |
#!C | |||
8 months ago |
364 |
Future future; | |||
365 |
Future_ctor(&future); | ||||
8 months ago |
366 |
||||
367 |
// keep &future to hand for when the background thing completes | ||||
8 months ago |
368 |
bool canceled = Future_Await(&future, NULL); | |||
8 months ago |
369 |
||||
8 months ago |
370 |
Future_dtor(&future); | |||
8 months ago |
371 |
||||
8 months ago |
372 |
`Future_New()` and `Future_Delete()` are also available if you prefer that style. | |||
8 months ago |
373 |
||||
374 |
Inside the callback when the background thing is complete: | ||||
375 |
|||||
8 months ago |
376 |
#!C | |||
8 months ago |
377 |
// result is a void * | |||
8 months ago |
378 |
Future_SetResult(future, result, false); | |||
8 months ago |
379 |
||||
380 |
or, if something went wrong: | ||||
381 |
|||||
8 months ago |
382 |
#!C | |||
8 months ago |
383 |
// exception is a void * | |||
8 months ago |
384 |
Future_SetResult(future, exception, true); | |||
8 months ago |
385 |
||||
386 |
Back in the task, you can respond to the future: | ||||
387 |
|||||
8 months ago |
388 |
#!C | |||
8 months ago |
389 |
... Future_Await has returned | |||
8 months ago |
390 |
if (canceled){ | |||
391 |
// exit quickly - you've been canceled | ||||
392 |
// you could, for example, use the future's result as an exception, or error code here | ||||
393 |
} | ||||
394 |
// carry on - the future's result may be an actual result, that's up to you | ||||
395 |
|||||
396 |
|||||
8 months ago |
397 |
##### void Future_ctor(Future *fut) | |||
8 months ago |
398 |
||||
8 months ago |
399 |
fut | |||
8 months ago |
400 |
: The `Future` being constructed | |||
8 months ago |
401 |
||||
8 months ago |
402 |
Initialise a future. When you no longer need it, use `Future_dtor()`. | |||
8 months ago |
403 |
||||
8 months ago |
404 |
##### Future *Future_New() | |||
8 months ago |
405 |
||||
8 months ago |
406 |
(returns) | |||
8 months ago |
407 |
: The new future | |||
8 months ago |
408 |
||||
8 months ago |
409 |
Allocates and initialises a future, When you no longer need it, use `Future_Delete()`. | |||
8 months ago |
410 |
||||
8 months ago |
411 |
##### void Future_dtor(Future *fut) | |||
8 months ago |
412 |
||||
413 |
fut | ||||
8 months ago |
414 |
: The `Future` being destructed | |||
8 months ago |
415 |
||||
8 months ago |
416 |
Destruct a future previously constructed with `Future_ctor()`. | |||
8 months ago |
417 |
||||
8 months ago |
418 |
##### void Future_Delete(Future *fut) | |||
8 months ago |
419 |
||||
8 months ago |
420 |
fut | |||
8 months ago |
421 |
: The `Future` to be destructed and freed | |||
8 months ago |
422 |
||||
8 months ago |
423 |
Delete (finalise and free) a future previously new'ed with `Future_New()` | |||
8 months ago |
424 |
||||
8 months ago |
425 |
##### void Future_SetResult(Future *fut, bool canceled, void *value) | |||
8 months ago |
426 |
||||
8 months ago |
427 |
fut | |||
8 months ago |
428 |
: The `Future` whose result is being set | |||
8 months ago |
429 |
||||
8 months ago |
430 |
canceled | |||
431 |
: The future's `canceled` setting | ||||
8 months ago |
432 |
||||
8 months ago |
433 |
value | |||
8 months ago |
434 |
: The future's result `value` | |||
8 months ago |
435 |
||||
8 months ago |
436 |
Set the result of a future. This has an effect only the first time its done, ie a completed future can't be canceled and a canceled future can't be completed. When an `Future` has a result, its watchers are called back. | |||
8 months ago |
437 |
||||
8 months ago |
438 |
The `value` of a future might be a result if the future completes (when `canceled == false`), or could be some sort of exception value if `canceled == true`. The interpretation of a future's `value` is up to the user - as far as the async system is concerned, it's only a `void *`. | |||
8 months ago |
439 |
||||
8 months ago |
440 |
##### bool Future_GetResult(Future *fut, void **res) | |||
8 months ago |
441 |
||||
8 months ago |
442 |
(returns) | |||
8 months ago |
443 |
: The `canceled` value of the `Future`. | |||
8 months ago |
444 |
||||
8 months ago |
445 |
res | |||
8 months ago |
446 |
: Where to store the value of the `Future`. This may be `NULL`. | |||
8 months ago |
447 |
||||
8 months ago |
448 |
Get the result of a future. | |||
8 months ago |
449 |
||||
8 months ago |
450 |
##### typedef void (*Future_Watcher)(void *me, Future *fut) | |||
8 months ago |
451 |
||||
8 months ago |
452 |
A `Future_Watcher` is a callback called when a future has a result. The `me` parameter is the one passed to `Future_AddWatcher()`. `fut` is the future which has just got its result. | |||
8 months ago |
453 |
||||
8 months ago |
454 |
##### void Future_AddWatcher(Future *fut, Future_Watcher watcher, void *me) | |||
8 months ago |
455 |
||||
8 months ago |
456 |
fut | |||
8 months ago |
457 |
: the `Future` to add a watcher to | |||
8 months ago |
458 |
||||
459 |
watcher | ||||
8 months ago |
460 |
: the callback to call when the `Future` has a result. | |||
8 months ago |
461 |
||||
462 |
me | ||||
463 |
: the `me` value to pass to `watcher` when it is called back. | ||||
464 |
|||||
465 |
Add a watcher (callback) to be called when the future has a result. If the future is already complete, `watcher` is immediately called. The `me` value is passed to the watcher as its `me` parameter. It is assumed that a watcher, identified by the `(watcher, me)` pair, will only be added once. | ||||
466 |
|||||
8 months ago |
467 |
##### void Future_RemoveWatcher(Future *fut, Future_Watcher watcher, void *me) | |||
8 months ago |
468 |
||||
469 |
fut | ||||
8 months ago |
470 |
: the `Future` to remove a watcher from | |||
8 months ago |
471 |
||||
472 |
watcher | ||||
473 |
: the callback of the watcher to remove. | ||||
474 |
|||||
475 |
me | ||||
476 |
: the `me` value of the watcher to remove. | ||||
477 |
|||||
478 |
Remove a watcher from a future. It is not an error if no watcher matching `(watcher, me)` is found - it has probably already been called back. | ||||
479 |
|||||
8 months ago |
480 |
##### bool Future_Await(Future *fut, void **res) | |||
8 months ago |
481 |
||||
482 |
(returns) | ||||
8 months ago |
483 |
: whether the `Future` was canceled. | |||
8 months ago |
484 |
||||
485 |
fut | ||||
8 months ago |
486 |
: The `Future` to wait for. | |||
8 months ago |
487 |
||||
488 |
res | ||||
489 |
: Where to store the `value` of the future when it is has a result. May be `NULL`. | ||||
490 |
|||||
8 months ago |
491 |
The current `Task` is paused until the `Future` has a result. Other `Task`s are run while this one is waiting. | |||
8 months ago |
492 |
||||
8 months ago |
493 |
##### typedef bool (*Task_Entry)(void *param, void **res) | |||
8 months ago |
494 |
||||
8 months ago |
495 |
The entry function to an `Task`. | |||
8 months ago |
496 |
||||
8 months ago |
497 |
##### void Task_ctor(Task *tsk, Task_Entry entry, void *param) | |||
8 months ago |
498 |
||||
499 |
tsk | ||||
500 |
: The task to construct. | ||||
501 |
|||||
502 |
entry | ||||
503 |
: The entry function for the task. | ||||
504 |
|||||
505 |
param | ||||
506 |
: The value for `param` to pass to `entry`. | ||||
507 |
|||||
8 months ago |
508 |
Initialises an `Task`. When you have finished with an `Task` you must finalise it using `Task_dtor()` | |||
8 months ago |
509 |
||||
8 months ago |
510 |
#!C | |||
8 months ago |
511 |
Task tsk; | |||
512 |
Task_ctor(&tsk, mytask, myparam); | ||||
8 months ago |
513 |
// tsk will run if you wait for a task or future | |||
8 months ago |
514 |
Task_Await(&tsk, NULL); | |||
515 |
Task_dtor(&tsk); | ||||
8 months ago |
516 |
||||
8 months ago |
517 |
##### Task *Task_New(Task_Entry entry, void *param) | |||
8 months ago |
518 |
||||
519 |
(returns) | ||||
8 months ago |
520 |
: The new `Task`. | |||
8 months ago |
521 |
||||
522 |
entry | ||||
523 |
: The entry function for the task. | ||||
524 |
|||||
525 |
param | ||||
526 |
: The value for `param` to pass to `entry`. | ||||
527 |
|||||
8 months ago |
528 |
This allocates and initialises a new `Task`. When you have finished with your task, you must `Task_Delete()` it. | |||
8 months ago |
529 |
||||
8 months ago |
530 |
#!C | |||
8 months ago |
531 |
Task *tsk = Task_New(mytask, myparam); | |||
8 months ago |
532 |
// tsk will run if you wait for a task or future | |||
8 months ago |
533 |
Task_Await(tsk, NULL); | |||
534 |
Task_Delete(tsk); | ||||
8 months ago |
535 |
||||
8 months ago |
536 |
##### void Task_dtor(Task *tsk) | |||
8 months ago |
537 |
||||
538 |
tsk | ||||
8 months ago |
539 |
: The `Task` to destruct. | |||
8 months ago |
540 |
||||
8 months ago |
541 |
This finalises an `Task` you ealier initalised with `Task_ctor()`. It is an error to attempt to destruct a task which is running. | |||
8 months ago |
542 |
||||
8 months ago |
543 |
#!C | |||
8 months ago |
544 |
Task tsk; | |||
545 |
Task_ctor(&tsk, mytask, myparam); | ||||
8 months ago |
546 |
// use tsk | |||
8 months ago |
547 |
Task_dtor(&tsk); | |||
8 months ago |
548 |
||||
8 months ago |
549 |
##### void Task_Delete(Task *tsk) | |||
8 months ago |
550 |
||||
551 |
tsk | ||||
8 months ago |
552 |
: The `Task` to delete. | |||
8 months ago |
553 |
||||
8 months ago |
554 |
This finalises and frees an `Task` you ealier new'ed with `Task_New()`. It is an error to attempt to delete a task which is running. | |||
8 months ago |
555 |
||||
8 months ago |
556 |
#!C | |||
8 months ago |
557 |
Task *tsk = Task_New(mytask, myparam); | |||
8 months ago |
558 |
// use tsk | |||
8 months ago |
559 |
Task_Delete(tsk); | |||
8 months ago |
560 |
||||
8 months ago |
561 |
##### static inline bool Task_Await(Task *tsk, void **res) | |||
8 months ago |
562 |
||||
563 |
(returns) | ||||
564 |
: Whether the task was canceled. | ||||
565 |
|||||
566 |
tsk | ||||
8 months ago |
567 |
: The `Task` to wait for. | |||
8 months ago |
568 |
||||
569 |
res | ||||
8 months ago |
570 |
: Where to store the `Task`'s value when it finishes. This may be NULL. | |||
8 months ago |
571 |
||||
8 months ago |
572 |
The current `Task` waits for `tsk` to finish, and returns the result. | |||
8 months ago |
573 |
||||
8 months ago |
574 |
##### void Task_Cancel(Task *tsk, void *cancel_value) | |||
8 months ago |
575 |
||||
576 |
tsk | ||||
577 |
: The task to cancel. | ||||
578 |
|||||
579 |
cancel_value | ||||
580 |
: The value to set on any future this task waits on. | ||||
581 |
|||||
582 |
This marks a task as canceled. When that task waits on a future that future will be canceled too, using `cancel_value`. | ||||
583 |
|||||
8 months ago |
584 |
##### static inline bool Task_IsCanceled(Task *tsk) | |||
8 months ago |
585 |
||||
586 |
(returns) | ||||
587 |
: Whether the task is canceled. | ||||
588 |
|||||
589 |
tsk | ||||
590 |
: The task to get its canceled setting from. | ||||
591 |
|||||
8 months ago |
592 |
##### static inline Future *Task_GetAwaitedFuture(Task *tsk) | |||
8 months ago |
593 |
||||
8 months ago |
594 |
(returns) | |||
595 |
: The future the task is waiting on. May be NULL. | ||||
596 |
|||||
597 |
tsk | ||||
598 |
: Teh task to read the future it is waiting on. | ||||
599 |
|||||
600 |
Return the future a task is waiting on. | ||||
601 |
|||||
8 months ago |
602 |
##### bool Task_Run(Task_Entry start, void *value, void **res) | |||
8 months ago |
603 |
||||
604 |
(returns) | ||||
8 months ago |
605 |
: Whether `start` was canceled. | |||
8 months ago |
606 |
||||
8 months ago |
607 |
start | |||
608 |
: The function to use as the main task. | ||||
8 months ago |
609 |
||||
610 |
value | ||||
8 months ago |
611 |
: The value to pass to `start`. | |||
8 months ago |
612 |
||||
8 months ago |
613 |
res | |||
614 |
: Where to store the result of `start`. | ||||
8 months ago |
615 |
||||
8 months ago |
616 |
Runs `start` as an `Task`. When `start` returns all other tasks must have been destructed, using `Task_dtor()` or `Task_Delete()`. | |||
8 months ago |
617 |
||||
8 months ago |
618 |
## ASleep | |||
8 months ago |
619 |
||||
8 months ago |
620 |
##### void ASleep_StartSystem() | |||
8 months ago |
621 |
||||
3 months ago |
622 |
You must start the `ASleep` system to use it. This needs to happen per process. Once you've finished with `ASleep` you must `ASleep_StopSystem()`. | |||
8 months ago |
623 |
||||
8 months ago |
624 |
#!C | |||
8 months ago |
625 |
ASleep_StartSystem(); | |||
626 |
// Now you can use ASleep() on any thread | ||||
627 |
ASleep_StopSystem(); | ||||
8 months ago |
628 |
||||
8 months ago |
629 |
##### void ASleep_StopSystem() | |||
8 months ago |
630 |
||||
8 months ago |
631 |
Call this to stop the `ASleep` system. | |||
8 months ago |
632 |
||||
8 months ago |
633 |
##### bool ASleep(float delay, void **value) | |||
8 months ago |
634 |
||||
8 months ago |
635 |
(returns) | |||
636 |
: Whether the task was canceled. | ||||
8 months ago |
637 |
||||
8 months ago |
638 |
delay | |||
639 |
: How many seconds to delay for. | ||||
8 months ago |
640 |
||||
8 months ago |
641 |
value | |||
642 |
: Where to store the cancellation value. This may be NULL. | ||||
8 months ago |
643 |
||||
8 months ago |
644 |
Sleep for `delay` seconds. `*value` will be set to `NULL` if the sleep is successful, and the `cancel_value` if the task is canceled. | |||
8 months ago |
645 |
||||
8 months ago |
646 |
## Generator | |||
8 months ago |
17 |
647 |
|||
8 months ago |
648 |
The pattern for a `Generator` is: | |||
649 |
|||||
8 months ago |
650 |
#### A loop which uses the `Generator | |||
8 months ago |
651 |
||||
8 months ago |
652 |
#!C | |||
8 months ago |
17 |
653 |
Generator gen; | ||
5 months ago |
654 |
Generator_ctor(&gen, generator_stack_size, mygen, ¶m); | |||
8 months ago |
17 |
655 |
|||
8 months ago |
656 |
void *value; | |||
657 |
while(Generator_Next(&gen, &value)){ | ||||
658 |
// use value here | ||||
8 months ago |
17 |
659 |
} | ||
8 months ago |
660 |
// value is now the return value from the Generator | |||
661 |
|||||
8 months ago |
17 |
662 |
Generator_dtor(&gen); | ||
663 |
|||||
8 months ago |
664 |
Or: | |||
8 months ago |
17 |
665 |
|||
8 months ago |
666 |
#!C | |||
5 months ago |
667 |
Generator *gen = Generator_New(generator_stack_size, mygen, ¶m); | |||
8 months ago |
17 |
668 |
|||
8 months ago |
669 |
void *value; | |||
670 |
while(Generator_Next(gen, &value)){ | ||||
671 |
// use value here | ||||
672 |
} | ||||
8 months ago |
17 |
673 |
|||
8 months ago |
674 |
Generator_Delete(gen); | |||
8 months ago |
17 |
675 |
|||
8 months ago |
676 |
`Generator`s yield a series of `void *`s - what the `void *`s mean is up to you. `Generator_Next()` returns a `bool` to indicate whether the `Generator` has finished. | |||
5 months ago |
677 |
The `generator_stack_size` is the stack amount made available to your generator. | |||
8 months ago |
17 |
678 |
|||
8 months ago |
679 |
#### A generator function | |||
8 months ago |
17 |
680 |
|||
8 months ago |
681 |
#!C | |||
8 months ago |
682 |
void *mygen(void *param){ | |||
683 |
bool domore = true; | ||||
684 |
// The parameter is a pointer to a string of chars | ||||
685 |
for (char *str = param; *str; ++str) { | ||||
686 |
// The value yielded is a pointer to a character in the string | ||||
687 |
domore = Generator_Yield(str); | ||||
688 |
if (!domore){ | ||||
689 |
break; | ||||
690 |
} | ||||
691 |
} | ||||
692 |
|||||
693 |
return (void *)domore; | ||||
8 months ago |
17 |
694 |
} | ||
695 |
|||||
8 months ago |
696 |
The `bool` returned from `Generator_Yield()` indicates whether the generator function should yield more values. When it is `false` the `Generator` is being finalised - your generator function should close files, and release any other resources it has claimed, before exiting. | |||
8 months ago |
17 |
697 |
|||
5 months ago |
698 |
##### void Generator_ctor(Generator *gen, size_t generator_stack_size, void *(*start)(void *), void *param) | |||
8 months ago |
17 |
699 |
|||
8 months ago |
700 |
gen | |||
701 |
: The `Generator` to construct. | ||||
8 months ago |
17 |
702 |
|||
5 months ago |
703 |
generator_stack_size | |||
704 |
: The amount of stack given to the generator. | ||||
705 |
|||||
8 months ago |
706 |
start | |||
707 |
: The function which is the start/entry-point of the `Generator`. | ||||
708 |
|||||
709 |
param | ||||
710 |
: The value to pass to `start`. | ||||
711 |
|||||
712 |
Initialise a `Generator`. When you no longer need the `Generator`, use `Generator_dtor()` to destruct it. | ||||
713 |
|||||
8 months ago |
714 |
#!C | |||
8 months ago |
715 |
Generator gen; | |||
5 months ago |
716 |
Generator_ctor(&gen, generator_stack_size, mystart, ¶ms); | |||
8 months ago |
17 |
717 |
|||
8 months ago |
718 |
// Generator is used | |||
8 months ago |
17 |
719 |
|||
8 months ago |
720 |
// ... later: | |||
721 |
Generator_dtor(&gen); | ||||
722 |
|||||
5 months ago |
723 |
##### Generator *Generator_New(size_t generator_stack_size, void *(*start)(void *), void *param) | |||
8 months ago |
724 |
||||
5 months ago |
725 |
generator_stack_size | |||
726 |
: The amount of stack to give to the generator. | ||||
727 |
|||||
8 months ago |
728 |
start | |||
729 |
: The function which is the start/entry-point of the `Generator`. | ||||
8 months ago |
730 |
||||
8 months ago |
731 |
param | |||
732 |
: The value to pass to `start`. | ||||
733 |
|||||
734 |
`new` a `Generator` - malloc, and initialise it. When you no longer need the `Generator` use `Generator_dtor` to finalise it. | ||||
735 |
|||||
8 months ago |
736 |
#!C | |||
8 months ago |
737 |
Generator *gen = Generator_New(mystart, ¶ms); | |||
738 |
|||||
739 |
// Generator is used | ||||
740 |
|||||
741 |
// ... later: | ||||
742 |
Generator_Delete(gen); | ||||
743 |
|||||
8 months ago |
744 |
##### void Generator_dtor(Generator *gen) | |||
8 months ago |
745 |
||||
8 months ago |
746 |
gen | |||
747 |
: The `Generator` to destruct. | ||||
748 |
|||||
8 months ago |
749 |
Finalise a `Generator`. Once a `Generator` is no longer needed, it must be finalised: | |||
750 |
|||||
8 months ago |
751 |
#!C | |||
8 months ago |
752 |
// earlier... | |||
753 |
Generator gen; | ||||
5 months ago |
754 |
Generator_ctor(&gen, generator_stack_size, mystart, ¶ms); | |||
8 months ago |
755 |
||||
756 |
// Generator is used | ||||
757 |
|||||
758 |
// the Generator is no longer needed | ||||
759 |
Generator_dtor(&gen); | ||||
760 |
|||||
761 |
|||||
8 months ago |
762 |
##### void Generator_Delete(Generator *gen) | |||
8 months ago |
763 |
||||
8 months ago |
764 |
gen | |||
765 |
: The `Generator` to delete. | ||||
766 |
|||||
8 months ago |
767 |
Finalise then `free()` a `Generator`. Once a `new`ed `Generator` is no longer needed, it must be deleted: | |||
768 |
|||||
8 months ago |
769 |
#!C | |||
8 months ago |
770 |
// earlier... | |||
771 |
Generator *gen = Generator_New(mystart, ¶ms); | ||||
772 |
|||||
773 |
// Generator is used | ||||
774 |
|||||
775 |
// the Generator is no longer needed | ||||
776 |
Generator_Delete(gen); | ||||
777 |
|||||
778 |
|||||
8 months ago |
779 |
##### bool Generator_Next(Generator *gen, void **value) | |||
8 months ago |
780 |
||||
8 months ago |
781 |
(returns) | |||
782 |
: Whether there is a next value. `true` - there is a next value; `false` - the `Generator` has finished | ||||
783 |
|||||
784 |
gen | ||||
785 |
: The `Generator` to get the next value from. | ||||
786 |
|||||
787 |
value | ||||
788 |
: Where to store the next value. | ||||
789 |
|||||
8 months ago |
790 |
Get the next value yielded by the `Generator`. | |||
791 |
|||||
8 months ago |
792 |
#!C | |||
8 months ago |
793 |
void *value; | |||
794 |
while(Generator_Next(gen, &value)){ | ||||
795 |
// use value here | ||||
8 months ago |
17 |
796 |
} | ||
8 months ago |
797 |
||||
8 months ago |
798 |
The `Generator` feeds values to its client using `Generator_Yield()` - it is these values which `Generator_Next()` sets, in the example, `value` to. | |||
8 months ago |
799 |
||||
8 months ago |
800 |
When a `Generator` is finished it returns from `start`. When you call `Generator_Yield()` on a finished `Generator` it returns `false` and `value` will be the return value from `start`. | |||
801 |
|||||
8 months ago |
802 |
##### bool Generator_Yield(void *value) | |||
8 months ago |
803 |
||||
8 months ago |
804 |
(returns) | |||
805 |
: Whether the `Generator` should do more. | ||||
8 months ago |
806 |
||||
8 months ago |
807 |
value | |||
808 |
: The `Generator`'s next value. | ||||
809 |
|||||
810 |
Yield a value from a `Generator`. | ||||
811 |
|||||
8 months ago |
812 |
#!C | |||
8 months ago |
813 |
bool domore = Generator_Yield(value); | |||
814 |
|||||
8 months ago |
815 |
`value` is then provided by `Generator_Next()` as the next value from the generator. | |||
8 months ago |
816 |
||||
8 months ago |
817 |
The `bool` returned by `Generator_Yield()` says whether more values should be provided by your generator function. `true` - provide more values if there are any. `false` - close files, free memory, free up any other resources and `return`. `false` is returned when the `Generator` is being finalised before it has finished, ie the client has exited its `for`-loop early. | |||
818 |
|||||
8 months ago |
819 |
## Coroutine | |||
820 |
|||||
3 months ago |
821 |
##### Coroutine_Err | |||
8 months ago |
822 |
||||
3 months ago |
823 |
The enum of errors: | |||
8 months ago |
824 |
||||
3 months ago |
825 |
Coroutine_OK | |||
826 |
: Everything is OK. This is 0 | ||||
8 months ago |
827 |
||||
3 months ago |
828 |
Coroutine_Err_SystemNotRunning | |||
829 |
: A `Coroutine` must be running to do this | ||||
8 months ago |
830 |
||||
3 months ago |
831 |
Coroutine_Err_SystemRunning | |||
832 |
: The `Coroutine` system must not be running to do this | ||||
8 months ago |
833 |
||||
3 months ago |
834 |
Coroutine_Err_NoStack | |||
835 |
: Not enough stack is available | ||||
836 |
|||||
837 |
Coroutine_Err_CoroutineFromWrongThread | ||||
838 |
: Trying to do something on one thread to a `Coroutine` from a different thread | ||||
839 |
|||||
840 |
Coroutine_Err_ACoroutineIsAlreadyRunning | ||||
841 |
: Trying `Coroutine_RunCoroutine` a `Coroutine` | ||||
842 |
|||||
843 |
Coroutine_Err_ExitWithRunningCoroutines | ||||
844 |
: All `Coroutine`s must be complete | ||||
845 |
|||||
846 |
Coroutine_Err_StackOverrun | ||||
847 |
: Stack overrun detected | ||||
848 |
|||||
849 |
Coroutine_Err_InternalInsistency | ||||
850 |
: Something didn't match inside the system | ||||
851 |
|||||
852 |
Coroutine_Err_CouldNotInitialiseSystem | ||||
853 |
: Something went wrong initialising (eg couldn't create a lock) | ||||
854 |
|||||
855 |
Coroutine_Err_WrongState | ||||
856 |
: It's in the wrong statem, eg trying to `Coroutine_Continue` a completed `Coroutine` | ||||
857 |
|||||
858 |
Coroutine_Err_Canceled | ||||
859 |
: It's canceled | ||||
860 |
|||||
6 months ago |
861 |
##### Coroutine_SetStackLimit(void *limit) | |||
862 |
|||||
5 months ago |
863 |
limit | |||
864 |
: The location (low address) of the stack's end. | ||||
865 |
|||||
6 months ago |
866 |
Set the limit of the stack. This is used to determine more accurately whether `Coroutine_CanStartCoroutine()` | |||
867 |
|||||
3 months ago |
868 |
##### Coroutine_Report Coroutine_GetReport() | |||
8 months ago |
869 |
||||
5 months ago |
870 |
(returns) | |||
871 |
: A report from this run of the Coroutine system. | ||||
872 |
|||||
8 months ago |
873 |
#!C | |||
8 months ago |
874 |
typedef struct Coroutine_Report { | |||
875 |
unsigned coroutines_created; | ||||
876 |
unsigned coroutines_pool_size; | ||||
877 |
unsigned lowest_headroom; | ||||
878 |
} Coroutine_Report; | ||||
879 |
|||||
880 |
coroutines_created | ||||
881 |
: How many coroutines were created | ||||
882 |
|||||
883 |
coroutines_pool_size | ||||
884 |
: The size of the coroutine pool (count of available, free `Coroutine` objects) when the system stopped. This is also the peak number of active coroutines. This will give you an idea of how much stack was needed for your coroutines. | ||||
885 |
|||||
886 |
lowest_headroom | ||||
3 months ago |
887 |
: The lowest headroom (unused | |||
8 months ago |
888 |
||||
3 months ago |
889 |
largest_stack | |||
890 |
: The largest stack requested for any `Coroutine`. | ||||
891 |
|||||
3 months ago |
892 |
##### Coroutine_CheckIntegrity() | |||
3 months ago |
893 |
||||
3 months ago |
894 |
(returns) | |||
895 |
: `Coroutine_Err` for any problem | ||||
3 months ago |
896 |
||||
3 months ago |
897 |
Check the integrity of the coroutine system, and `printf()` any problems. | |||
898 |
|||||
8 months ago |
899 |
##### Coroutine_Start | |||
8 months ago |
900 |
||||
8 months ago |
901 |
#!C | |||
8 months ago |
902 |
void *(*)(void *param) | |||
903 |
|||||
8 months ago |
904 |
The entry function for a coroutine. The `param` is the value passed to `Coroutine_Continue`, and the `void *` return value can be accessed through the `Coroutine` object using `Coroutine_GetValue()`. | |||
8 months ago |
905 |
||||
3 months ago |
906 |
##### Coroutine_SystemStart | |||
907 |
|||||
908 |
#!C | ||||
909 |
Coroutine_Err (*)(void *) | ||||
910 |
|||||
911 |
The entry function for `Coroutine_RunSystem`. | ||||
912 |
|||||
913 |
##### Coroutine_Err Coroutine_RunSystem(Coroutine_SystemStart start, void *value) | ||||
914 |
|||||
915 |
(returns) | ||||
916 |
: `Coroutine_OK` or an error. If the system starts, this will be the value returned by `start`. | ||||
917 |
|||||
918 |
start | ||||
919 |
: The function to call with the `Coroutine` system started. It is expected that this routine will | ||||
920 |
start a `Coroutine`. | ||||
921 |
|||||
922 |
value | ||||
923 |
: The value to pass to `start`. | ||||
924 |
|||||
5 months ago |
925 |
##### Coroutine *Coroutine_New(size_t size, Coroutine_Start start) | |||
8 months ago |
926 |
||||
5 months ago |
927 |
(returns) | |||
928 |
A new Coroutine, or `NULL` if there was a failure, such as insufficient stack for the new Coroutine. | ||||
929 |
|||||
930 |
size | ||||
931 |
: The stack size given to this Coroutine. | ||||
932 |
|||||
933 |
start | ||||
934 |
: The routine called to start the Coroutine. | ||||
935 |
|||||
5 months ago |
936 |
Create a new `Coroutine`. The `Coroutine` system must be started to create a `Coroutine`. The stack size available to the coroutine will be `COROUTINE_STACK_SIZE` defined in `coroutine.h`. When you have finished with your `Coroutine`, use `Coroutine_Delete()` to delete it. If there is not enough space for a new `Coroutine` on your stack, `NULL` will be returned. | |||
8 months ago |
937 |
||||
3 months ago |
938 |
##### Coroutine_Err Coroutine_Run_Coroutine(Coroutine *cor, void *value) | |||
8 months ago |
939 |
||||
3 months ago |
940 |
(returns) | |||
941 |
: any problem, or `Coroutine_OK` | ||||
942 |
|||||
5 months ago |
943 |
cor | |||
944 |
: The Coroutine to run. | ||||
945 |
|||||
946 |
value | ||||
947 |
: The value to pass to the Coroutine's `start` routine. | ||||
948 |
|||||
6 months ago |
949 |
Run the `Coroutine` and return when it returns. This is how to start coroutines running in the coroutine system. It is an error for the run coroutine to return before all other coroutines have completed, and the coroutine system must be started to call this. | |||
8 months ago |
950 |
||||
3 months ago |
951 |
##### Coroutine_Err Coroutine_Run(size_t size, Coroutine_Start start, void *value, void **result) | |||
8 months ago |
952 |
||||
5 months ago |
953 |
(returns) | |||
3 months ago |
954 |
: `Coroutine_OK` or any problem | |||
5 months ago |
955 |
||||
956 |
size | ||||
957 |
: The stack size to give to the Corotuine. | ||||
958 |
|||||
959 |
start | ||||
960 |
: The routine to start the Coroutine. | ||||
961 |
|||||
962 |
value | ||||
963 |
: The value to pass to `start()`. | ||||
964 |
|||||
965 |
result | ||||
966 |
: Where to store the return value from `start(value)`. This may be `NULL`. | ||||
967 |
|||||
5 months ago |
968 |
`start(value)` is called from within a coroutine and its value returned in `*result`. | |||
969 |
If this completes without any failure, `false` is returned, otherwise, typically | ||||
970 |
because `Coroutine_New()` returned `NULL`, `true` is returned. `result` may be `NULL` if you don't | ||||
971 |
need the resturn value from `start()`. | ||||
972 |
When the coroutine system is active - you are already running in a coroutine - `start(value)` | ||||
973 |
is simply called and its result returned in `*result`. When the Coroutine system is not running, | ||||
974 |
`Coroutine_Run()` starts it, creates a `Coroutine` and runs that Coroutine to call `start(calue)` | ||||
5 months ago |
975 |
and return value is returned in `*result`, then stops the Coroutine system. If you need to force | |||
976 |
a new Coroutine to be created, with a particular stack size to call `start(value)`, then use | ||||
977 |
`Coroutine_Chain()` instead. | ||||
8 months ago |
978 |
||||
3 months ago |
979 |
The total stack allowed for all coroutines running on any thread is the size of the call stack on that thread. | |||
980 |
|||||
8 months ago |
981 |
##### void Coroutine_Delete(Coroutine *cor) | |||
8 months ago |
982 |
||||
5 months ago |
983 |
cor | |||
984 |
: The Coroutine to delete. | ||||
985 |
|||||
8 months ago |
986 |
Use `Coroutine_Delete()` to delete a coroutine when it is no longer needed. It is an error to attempt to delete a coroutine which is running. | |||
8 months ago |
987 |
||||
3 months ago |
988 |
##### Coroutine_Err Coroutine_Continue(Coroutine *cor, void *value, bool early) | |||
8 months ago |
989 |
||||
3 months ago |
990 |
(returns) | |||
3 months ago |
991 |
: `Coroutine_OK` or any error. | |||
3 months ago |
992 |
||||
5 months ago |
993 |
cor | |||
994 |
: The Coroutine to continue. | ||||
995 |
|||||
996 |
value | ||||
997 |
: The value to return from `cor`'s yield function. | ||||
998 |
|||||
999 |
early | ||||
1000 |
: Whether to continue `cor` early (`true`), or late (`false`). Early means before other Coroutines which are waiting | ||||
1001 |
to be called, whereas late means after them. | ||||
1002 |
|||||
3 months ago |
1003 |
Continue the given `Coroutine`. `value` is passed to the coroutine, as `param` to the `start` function, or as the return value from `Coroutine_Yield`. `early` determines whether the continued coroutine will be run next, or after all the other, currently runnable, coroutines. If the `Coroutine` is already runnable, nothing is done, and `false` is returned. If the `Coroutine` is free, or complete, nothing is done and `true` is returned to show there was a problem. | |||
8 months ago |
1004 |
||||
8 months ago |
1005 |
##### void *Coroutine_Yield(void *value, Coroutine_YieldCallback on_yield, void *this) | |||
8 months ago |
1006 |
||||
5 months ago |
1007 |
value | |||
1008 |
: The value to yield fropm the coroutine. | ||||
1009 |
|||||
1010 |
on_yield | ||||
1011 |
: A callback to be called once this Coroutine has yielded, but before another one has been continued. | ||||
1012 |
|||||
1013 |
this | ||||
1014 |
: The parameter to pass to `on_yield`. | ||||
1015 |
|||||
8 months ago |
1016 |
Yield `value` from the current coroutine; this coroutine is moved to the list of coroutines waiting to be continued. The next runable coroutine is run - either by its start routine being called with `value` as its `param`, or by `value`being returned from its `Coroutine_Yield()`. | |||
1017 |
|||||
8 months ago |
1018 |
##### void *Coroutine_GetValue(Coroutine *cor) | |||
8 months ago |
1019 |
||||
5 months ago |
1020 |
(returns) | |||
1021 |
: The Coroutine's value - the last yielded or returned value. | ||||
1022 |
|||||
1023 |
cor | ||||
1024 |
: The Coroutine to query. | ||||
1025 |
|||||
8 months ago |
1026 |
Return the `Coroutine`'s value - the value last yielded, or returned by its `start` routine. | |||
1027 |
|||||
8 months ago |
1028 |
##### Coroutine *Coroutine_GetActive() | |||
8 months ago |
1029 |
||||
5 months ago |
1030 |
(returns) | |||
1031 |
: The currently active Coroutine. | ||||
1032 |
|||||
8 months ago |
1033 |
Return whihc coroutine is currently running, ie the caller's `Coroutine`. | |||
1034 |
|||||
8 months ago |
1035 |
##### bool Coroutine_IsRunning(Coroutine *cor) | |||
8 months ago |
1036 |
||||
5 months ago |
1037 |
(returns) | |||
1038 |
: Whether `cor` is running - it's the active coroutine or waiting to be continued. | ||||
1039 |
|||||
1040 |
cor | ||||
1041 |
: The Coroutine to query. | ||||
1042 |
|||||
8 months ago |
1043 |
Return whether the given coroutine is still running - it may be running, ready to run, or waiting to be continued, but won't have returned from its `start` function. | |||
8 months ago |
1044 |
||||
6 months ago |
1045 |
##### bool Coroutine_IsComplete(Coroutine *cor) | |||
1046 |
|||||
5 months ago |
1047 |
(returns) | |||
1048 |
: Whether `cor` is complete, ie has returned from `start()`./ | ||||
1049 |
|||||
1050 |
cor | ||||
1051 |
: The Coroutine to query. | ||||
1052 |
|||||
6 months ago |
1053 |
Return whether the given coroutine is complete - is has returned from its `start` function. | |||
1054 |
|||||
7 months ago |
1055 |
##### intptr_t Coroutine_GetStackHeadroom() | |||
8 months ago |
1056 |
||||
5 months ago |
1057 |
(returns) | |||
1058 |
: The amount of stack headroom. | ||||
1059 |
|||||
8 months ago |
1060 |
Return the headroom available in the current coroutine's stack. This can be used to detect when your coroutine is nearing its stack limit, and then use `Coroutine_Chain()` to continue in a new chunk of coroutine stack. | |||
8 months ago |
1061 |
||||
5 months ago |
1062 |
##### bool Coroutine_CanStartCoroutine(size_t size) | |||
8 months ago |
1063 |
||||
5 months ago |
1064 |
(returns) | |||
1065 |
: Whether a Coroutine with the given amount of stack could be created. | ||||
1066 |
|||||
1067 |
size | ||||
1068 |
: The amount of stack in the Coroutine we might want to create. | ||||
1069 |
|||||
6 months ago |
1070 |
Return whether the coroutine system can start a new coroutine. This check can only be done with the coroutine system active (currently running | |||
6 months ago |
1071 |
a coroutine). If there's a free coroutine, or enough space on the stack for a new one, then this will return `true`. To set the limit of the | |||
1072 |
stack use `Coroutine_SetStackLimit()` | ||||
8 months ago |
1073 |
||||
6 months ago |
1074 |
##### void *Coroutine_GetStackHWM(void) | |||
1075 |
|||||
5 months ago |
1076 |
(returns) | |||
1077 |
: The lowest address where the active Coroutine's stack has grown to ever. | ||||
1078 |
|||||
6 months ago |
1079 |
Find out where this coroutine's guard patterns end. This is intended as a part of the tools to measure how much stack something is using: | |||
1080 |
|||||
1081 |
#!C | ||||
1082 |
Coroutine_ClearStackForHWM(); | ||||
1083 |
char *before = (char *)Coroutine_GetStackHWM(); | ||||
1084 |
// do the thing you want to measure here | ||||
1085 |
char *after = (char *)Coroutine_GetStackHWM(); | ||||
1086 |
intptr_t amount_used = before - after; | ||||
1087 |
|||||
1088 |
##### void Coroutine_ClearStackForHWM(void) | ||||
1089 |
|||||
1090 |
Fill the unused stack in this coroutine with a guard pattern. This is intended as a part of the tools to measure how much stack something is using: | ||||
1091 |
|||||
1092 |
#!C | ||||
1093 |
Coroutine_ClearStackForHWM(); | ||||
1094 |
char *before = (char *)Coroutine_GetStackHWM(); | ||||
1095 |
// do the thing you want to measure here | ||||
1096 |
char *after = (char *)Coroutine_GetStackHWM(); | ||||
1097 |
intptr_t amount_used = before - after; | ||||
1098 |
|||||
8 months ago |
1099 |
##### void *Coroutine_GetCStackTop() | |||
8 months ago |
1100 |
||||
5 months ago |
1101 |
(returns) | |||
1102 |
: Where the Coroutine system has reached in the C stack. | ||||
1103 |
|||||
8 months ago |
1104 |
Return an address which is near to the top of used C stack. | |||
1105 |
|||||
3 months ago |
1106 |
##### Coroutine_Err Coroutine_Chain(size_t size, Coroutine_Start start, void *value, void **result) | |||
8 months ago |
1107 |
||||
5 months ago |
1108 |
(returns) | |||
3 months ago |
1109 |
: Whether there was a problem. `Coroutine_OK` - `start(value)` was run; an error - there was a problem. | |||
5 months ago |
1110 |
||||
1111 |
size | ||||
1112 |
: The amount of stack to give the chained Coroutine. | ||||
1113 |
|||||
1114 |
start | ||||
1115 |
: The entry point ot the chained Coroutine. | ||||
1116 |
|||||
1117 |
value | ||||
1118 |
: The value to pass to `start()` | ||||
1119 |
|||||
1120 |
result | ||||
1121 |
: Where to store the return value from `start(value)`. This may be `NULL`. | ||||
1122 |
|||||
5 months ago |
1123 |
Run `start` with `value` on a new coroutine, and return its return value in `*result`. `result` | |||
1124 |
may be NULL. `Coroutine_Run()` returns `false` if nothing fails, and `true` if something went wrong, | ||||
5 months ago |
1125 |
usually when `Coroutine_New()` ran out of stack. `stack_size` is the amount of stack made available | |||
1126 |
to the chained Coroutine. | ||||
5 months ago |
1127 |
It is expected that `Coroutine_Chain()` will be used when your coroutine is running short | |||
1128 |
of stack - it is not an alternative to `Coroutine_Run()`. | ||||
5 months ago |
1129 |
||||
6 days ago |
1130 |
##### void Coroutine_Dump_() | |||
5 months ago |
1131 |
||||
1132 |
*Do not use this function in production code* | ||||
1133 |
|||||
1134 |
This prints the current state of the Coroutine system. It is used for development, and is not part of the official interface. | ||||
1135 |