0 branches 0 tags
25 26
27
28 29
Documentation experiment
on 8:16 AM Sep 19 2025
README.md
26
27
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
Stackful coroutines in C.
* `Async` coroutines which can pause, waiting for a future.
* `Generator` coroutines used as generators for loops.
* `Coroutine` the coroutine engine used by `Async` and `Generator`.
Your code doesn't need to do anything special to be a coroutine, and only standard, or commonly available libraries are needed.
## Prerequisites
The goal was to make a system which can be used 'out of the box'. 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.
You will need to build & link the code as part of your system - `coroutine/async.c`, `coroutine/generator.c` and `coroutine/coroutine.c` - ensure the headers, `include/*`, are available on your include path.
## Quick Start
### Async
To run an Async program:
#include "async.h"
main(){
Async_StartSystem();
void *res = NULL;
bool canceled = Async_Run(asyncmain, &param, &res);
Async_StopSystem();
}
Async runs tasks, switching between them when the current task waits on an `Async_Future`. `asyncmain()` is run as a task. The start function for any task looks like this:
bool mytask(void *param, void **res){
// do your thing here
return canceled;
}
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.
Within your async task, create `Async_Task`s and `Async_Task_Await()` them when you want to wait for their result:
Async_Task task1;
Async_Task_ctor(&task1, adifferenttask, &task1param);
void *result;
bool canceled = Async_Task_Await(&task1, &result);
Async_Task_dtor(&task1);
// use the result
When a task needs to wait for something, and wants to allow other tasks to run, it should use a `Future`:
Async_Future future;
Async_Future_ctor(&future);
// pass the future to the background-thing-which-might-take-a-while
bool canceled = Async_Future_Await(&future);
// get the result, if you want it, from the future
Async_Future_dtor(&future);
When the background-thing-which-might-take-a-while has a result:
Async_Future_SetResult(future, false, result);
### Generators
The coroutine system needs to be started, either through `Async_StartSystem()`, or directly with `Coroutine_StartSystem()` if you don't want to do async things.
You will need a generator function:
void *yield_my_things(void *param){
bool domore = true;
// loop/call functions to find more values to yield, and when you have one:
domore = Generator_Yield(thing);
// .. if domore is false, exit your generator - it is being destructed
// not actually used by generators, but this is a useful convention for bubbling
// the flag out to calling functions.
return (void *)domore;
}
And to use it:
Generator gen;
Generator_ctor(&gen, yield_my_things, "..");
void *thing;
while(Generator_Next(&gen, &thing)){
// use thing - a value yielded by your generator
}
Generator_dtor(&gen);
### Coroutines
While you can use coroutines directly, it's designed as a system to support more useful patterns, like `Async` and `Generators`.
The Coroutines system must be started:
Coroutine_StartSystem();
// use coroutines here
Coroutine_StopSystem();
Your coroutine will need to have a start function:
void *start(void *param){
...
}
When there is no coroutine running, start your 'main' coroutine:
void *result = Coroutine_Run(comain, param);
Create other coroutines like this:
Coroutine *cor = Coroutine_New(start);
When you want a Coroutine to run, or to return from a yield:
Coroutine_Continue(cor, value, run_early);
`value` will be start function's parameter, or the value returned from the yield.
Within the Coroutine, to yield a value:
void *Coroutine_Yield(value, on_yield, void *me);
The on_yield function is called after the coroutine has been 'wait'ed, but before the next coroutine is resumed.
## How it Works
The coroutine system uses the stack, divided into smaller stacks, for the coroutines. This means you may need to consider whether the coroutine stack size, set by `COROUTINE_STARTUP_STACK_SIZE`, is right for your coroutines, and whether your stack size is enough for the number of coroutines you might run concurrently.
As each of your thread has its own stack - the coroutine system can be run (or not) independantly on each of your threads. For some special cases, you may want to adjust each of your thread's stack sizes depending on how it is used.
## Style
The style is influenced by C++. For example, where possible, a `Something *Something_New(a, b, c)` and `Something_Delete(Something *)`, where a `Something` is `malloc`ed, 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()`.
Something *oneofthem = Something_New();
// use oneofthem
Something_Delete(oneofthem);
Can be also be done like this, and this will run faster:
Something oneofthem;
Something_ctor(&oneofthem);
// use oneofthem
Something_dtor(&oneofthem);
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.
## Usage
When you are using coroutines or generators:
void *myfunc(void *){
// your function here
}
Coroutine_StartSystem();
Coroutine_Run(myfunc, (void *)myparam);
Coroutine_StopSystem();
If you also use async, then:
bool myfunc(void *myparam, void **res){
// your async function here
}
Async_StartSystem();
void *res = NULL;
bool canceled = Async_Run(myfunc, myparam, &res);
Async_StopSystem();
While the system is started, you can make many calls to `Coroutine_Run()` or `Async_Run()`. A running system is thread local - each thread you want to use coroutines on will need to be `Coroutine_StartSystem()`ed or `Async_StartSystem()`ed.
# API
## Generator
The pattern for a `Generator` is:
#### A loop which uses the `Generator`
Generator gen;
Generator_ctor(&gen, mygen, &param);
void *value;
while(Generator_Next(&gen, &value)){
// use value here
}
// value is now the return value from the Generator
Generator_dtor(&gen);
Or:
Generator *gen = Generator_New(mygen, &param);
void *value;
while(Generator_Next(gen, &value)){
// use value here
}
Generator_Delete(gen);
`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.
#### A generator function
void *mygen(void *param){
bool domore = true;
// The parameter is a pointer to a string of chars
for (char *str = param; *str; ++str) {
// The value yielded is a pointer to a character in the string
domore = Generator_Yield(str);
if (!domore){
break;
}
}
return (void *)domore;
}
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.
### void Generator_ctor(Generator *gen, void *(*start)(void *), void *param)
Initialise a `Generator`
Generator gen;
Generator_ctor(&gen, mystart, &params);
// Generator is used
// ... later:
Generator_dtor(&gen);
### Generator *Generator_New(void *(*)(void *), void *)
`new` a `Generator` - malloc it and initialise it.
Generator *gen = Generator_New(mystart, &params);
// Generator is used
// ... later:
Generator_Delete(gen);
### void Generator_dtor(Generator *gen)
Finalise a `Generator`. Once a `Generator` is no longer needed, it must be finalised:
// earlier...
Generator gen;
Generator_ctor(&gen, mystart, &params);
// Generator is used
// the Generator is no longer needed
Generator_dtor(&gen);
### void Generator_Delete(Generator *)
Finalise then `free()` a `Generator`. Once a `new`ed `Generator` is no longer needed, it must be deleted:
// earlier...
Generator *gen = Generator_New(mystart, &params);
// Generator is used
// the Generator is no longer needed
Generator_Delete(gen);
### bool Generator_Next(Generator *, void **value)
Get the next value yielded by the `Generator`.
void *value;
while(Generator_Next(gen, &value)){
// use value here
}
When `true` is returned, `value` is the value yielded by the `Generator`. When `false` is returned, `value` is the value returned when the `Generator` returned - the `Generator` has finished.
### bool Generator_Yield(void *)
Yield a value from a `Generator` function.
bool domore = Generator_Yield(value);
`value` is then provided by `Generator_Next()` as the next value from the generator. The `bool` returned by `Generator_Yield()` indicates whether more values should be provided by your generator function. When `true` provide more values if possible. When `false` close files, free memory, free up any other resources and `return`. `false` is returned when the `Generator` is being finalised.
## Coroutine
### void Coroutine_StartSystem();
Start the coroutine system on this thread. When you've finished with `Coroutine` call `Coroutine_Stop()`. `Coroutine` can be started & stopped many times on one thread. The total stack allowed for all coroutines running on a thread is the size of the call stack on that thread.
### void Coroutine_StopSystem();
Stop the coroutine system on this thread.
### Coroutine_Start
void *(*)(void *param)
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.
### Coroutine *Coroutine_New(Coroutine_Start start)
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.
### void Coroutine_Run_Coroutine(Coroutine *cor, void *value)
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.
### void *Coroutine_Run(Coroutine_Start start, void *value)
Convenience wrapper for `Coroutine_Run_Coroutine` which creates the `Coroutine` and retrieves its result.
### void Coroutine_Delete(Coroutine *cor)
Use `Coroutine_Delete()` to delete a coroutine when it is no longer needed.
### void Coroutine_Continue(Coroutine *cor, void *value, bool early)
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 is added to the head of tail of the list of runable coroutines.
### void *Coroutine_Yield(void *value, Coroutine_YieldCallback on_yield, void *this)
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()`.
### void *Coroutine_GetValue(Coroutine *cor)
Return the `Coroutine`'s value - the value last yielded, or returned by its `start` routine.
### Coroutine *Coroutine_GetActive()
Return whihc coroutine is currently running, ie the caller's `Coroutine`.
### bool Coroutine_IsRunning(Coroutine *cor)
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.
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
Stackful coroutines in C.
* `Async` coroutines which can pause, waiting for a future.
* `Generator` coroutines used as generators for loops.
* `Coroutine` the coroutine engine used by `Async` and `Generator`.
Your code doesn't need to do anything special to be a coroutine, and only standard, or commonly available libraries are needed.
## Prerequisites
The goal was to make a system which can be used 'out of the box'. 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.
You will need to build & link the code as part of your system - `coroutine/async.c`, `coroutine/generator.c` and `coroutine/coroutine.c` - ensure the headers, `include/*`, are available on your include path.
## Quick Start
### Async
To run an Async program:
#include "async.h"
main(){
Async_StartSystem();
void *res = NULL;
bool canceled = Async_Run(asyncmain, &param, &res);
Async_StopSystem();
}
Async runs tasks, switching between them when the current task waits on an `Async_Future`. `asyncmain()` is run as a task. The start function for any task looks like this:
bool mytask(void *param, void **res){
// do your thing here
return canceled;
}
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.
Within your async task, create `Async_Task`s and `Async_Task_Await()` them when you want to wait for their result:
Async_Task task1;
Async_Task_ctor(&task1, adifferenttask, &task1param);
void *result;
bool canceled = Async_Task_Await(&task1, &result);
Async_Task_dtor(&task1);
// use the result
When a task needs to wait for something, and wants to allow other tasks to run, it should use a `Future`:
Async_Future future;
Async_Future_ctor(&future);
// pass the future to the background-thing-which-might-take-a-while
void *res;
bool canceled = Async_Future_Await(&future, &res);
Async_Future_dtor(&future);
When the background-thing-which-might-take-a-while has a result:
Async_Future_SetResult(future, false, result);
### Generators
The coroutine system needs to be started, either through `Async_StartSystem()`, or directly with `Coroutine_StartSystem()` if you don't want to do async things.
You will need a generator function:
void *yield_my_things(void *param){
bool domore = true;
// loop/call functions to find more values to yield, and when you have one:
domore = Generator_Yield(thing);
// .. if domore is false, exit your generator - it is being destructed
// not actually used by generators, but this is a useful convention for bubbling
// the flag out to calling functions.
return (void *)domore;
}
And to use it:
Generator gen;
Generator_ctor(&gen, yield_my_things, "..");
void *thing;
while(Generator_Next(&gen, &thing)){
// use thing - a value yielded by your generator
}
Generator_dtor(&gen);
### Coroutines
While you can use coroutines directly, it's designed as a system to support more useful patterns, like `Async` and `Generators`.
The Coroutines system must be started:
Coroutine_StartSystem();
// use coroutines here
Coroutine_StopSystem();
Your coroutine will need to have a start function:
void *start(void *param){
...
}
When there is no coroutine running, start your 'main' coroutine:
void *result = Coroutine_Run(comain, param);
Create other coroutines like this:
Coroutine *cor = Coroutine_New(start);
When you want a Coroutine to run, or to return from a yield:
Coroutine_Continue(cor, value, run_early);
`value` will be start function's parameter, or the value returned from the yield.
Within the Coroutine, to yield a value:
void *Coroutine_Yield(value, on_yield, void *me);
The on_yield function is called after the coroutine has been 'wait'ed, but before the next coroutine is resumed.
## How it Works
The coroutine system uses the stack, divided into smaller stacks, for the coroutines. This means you may need to consider whether the coroutine stack size, set by `COROUTINE_STARTUP_STACK_SIZE`, is right for your coroutines, and whether your stack size is enough for the number of coroutines you might run concurrently.
As each of your thread has its own stack - the coroutine system can be run (or not) independantly on each of your threads. For some special cases, you may want to adjust each of your thread's stack sizes depending on how it is used.
## Style
The style is influenced by C++. For example, where possible, a `Something *Something_New(a, b, c)` and `Something_Delete(Something *)`, where a `Something` is `malloc`ed, 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()`.
Something *oneofthem = Something_New();
// use oneofthem
Something_Delete(oneofthem);
Can be also be done like this, and this will run faster:
Something oneofthem;
Something_ctor(&oneofthem);
// use oneofthem
Something_dtor(&oneofthem);
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.
## Usage
When you are using coroutines or generators:
void *myfunc(void *){
// your function here
}
Coroutine_StartSystem();
Coroutine_Run(myfunc, (void *)myparam);
Coroutine_StopSystem();
If you also use async, then:
bool myfunc(void *myparam, void **res){
// your async function here
}
Async_StartSystem();
void *res = NULL;
bool canceled = Async_Run(myfunc, myparam, &res);
Async_StopSystem();
While the system is started, you can make many calls to `Coroutine_Run()` or `Async_Run()`. A running system is thread local - each thread you want to use coroutines on will need to be `Coroutine_StartSystem()`ed or `Async_StartSystem()`ed.
## Stack Overruns
The C stack is divided down into smaller stacks. There's one to give some work room between `..StartSystem()` and `..Run()`, and one for each coroutine. 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:
* Use less stack. This is, sometimes, the right advice, especially if the startup stack overrins. The expectation is that very little is done between `.._StartSystem()` and `..Run()`. If your situation needs more doing, you can...
* increase the stack size. Adjust `COROUTINE_STACK_SIZE` (for startup) or `COROUTINE_STARTUP_STACK_SIZE` (for coroutines) as appropriate. 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...
* monitor stack headroom, and add another stack chunk if you need to:
In this last case you'll need to add some code at key points:
void *myfunction(void *param){
if (Coroutine_GetStackHeadroom() < MIN_ALLOWED_STACK){
return Coroutine_Chain(myfunction, param);
}
// do everything normally
}
More realistically:
struct myfunctionparams {
int a;
char *b;
struct dog *d;
}
void *mychain(void *param){
struct myfunctionparams *myparams = (struct myfunctionparams *)params;
return (void *)myfunction(myparams->a, myparams->b, *myparams->d);
}
int myfunction(int a, char *b, struct dog d){
if (Coroutine_GetStackHeadroom() < MIN_ALLOWED_STACK){
struct myfunctionparams params = {
a,
b,
&d
};
return (int)Coroutine_Chain(mychain, &params);
}
}
# API
## Async
The pattern for using async is:
void *myasyncmaintask(void *param){
// do your main async task things here, like starting more tasks
}
Async_StartSystem();
void *res = NULL;
bool canceled = Async_Run(myasyncmaintask, NULL, &res);
Async_StopSystem();
To create and wait for an async task:
Async_Task task1;
Async_Task_ctor(&task1, asynctask1, &task1param);
void *res = NULL;
bool canceled = Async_Task_Await(&task1, void **res)
Async_Task_dtor(&task1);
or, if you prefer new & delete:
Async_Task *task1 = Async_Task_New(asynctask1, &task1param);
void *res = NULL;
bool canceled = Async_Task_Await(task1, void **res)
Async_Task_Delete(task1);
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:
Async_Future future;
Async_Future_ctor(&future);
// keep &future to hand for when the background thing completes
bool canceled = Async_Future_Await(&future, NULL);
Async_Future_dtor(&future);
`Async_Future_New()` and `Async_Future_Delete()` are also available if you prefer that style.
Inside the callback when the background thing is complete:
// result is a void *
Async_Future_SetResult(future, result, false);
or, if something went wrong:
// exception is a void *
Async_Future_SetResult(future, exception, true);
Back in the task, you can respond to the future:
... Async_Future_Await has returned
if (canceled){
// exit quickly - you've been canceled
// you could, for example, use the future's result as an exception, or error code here
}
// carry on - the future's result may be an actual result, that's up to you
### void Async_Future_ctor(Async_Future *fut)
Initialise a future. When you no longer need it, use `Async_Future_dtor()`.
### Async_Future *Async_Future_New()
Allocate and initialise a future, When you no longer need it, use `Async_Future_Delete()`.
Returns the new `Async_Future`
#### `void Async_Future_dtor(Async_Future *fut)`
fut
: The `Async_Future` being destructed
Destruct a future previously constructed with `Async_Future_ctor()`.
### void Async_Future_Delete(Async_Future *fut)
Delete (finalise and free) a future previously new'ed with `Async_Future_New()`
### void Async_Future_SetResult(Async_Future *fut, bool canceled, void *value)
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.
### bool Async_Future_GetResult(Async_Future *fut, void **res)
Get the result of a future. The return value is whether it was canceled. `res` may be `NULL`.
### typedef void (*Future_Watcher)(void *me, Async_Future *fut)
A `Future_Watcher` is a callback called when a future is done. The `me` parameter is the one passed to `Async_Future_AddWatcher()`. `fut` is the future which has just completed.
### void Async_Future_AddWatcher(Async_Future *fut, Future_Watcher watcher, void *me)
Add a watcher (callback) to be called when the future is done. If the future is already complete, `watcher` is immediately called. The `me` value is passed to the watcher as its `me` parameter.
### void Async_Future_RemoveWatcher(Async_Future *fut, Future_Watcher watcher, void *me)
Remove a watcher from a future.
### bool Async_Future_Await(Async_Future *fut, void **res)
## Generator
The pattern for a `Generator` is:
#### A loop which uses the `Generator`
Generator gen;
Generator_ctor(&gen, mygen, &param);
void *value;
while(Generator_Next(&gen, &value)){
// use value here
}
// value is now the return value from the Generator
Generator_dtor(&gen);
Or:
Generator *gen = Generator_New(mygen, &param);
void *value;
while(Generator_Next(gen, &value)){
// use value here
}
Generator_Delete(gen);
`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.
#### A generator function
void *mygen(void *param){
bool domore = true;
// The parameter is a pointer to a string of chars
for (char *str = param; *str; ++str) {
// The value yielded is a pointer to a character in the string
domore = Generator_Yield(str);
if (!domore){
break;
}
}
return (void *)domore;
}
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.
### void Generator_ctor(Generator *gen, void *(*start)(void *), void *param)
Initialise a `Generator`
Generator gen;
Generator_ctor(&gen, mystart, &params);
// Generator is used
// ... later:
Generator_dtor(&gen);
### Generator *Generator_New(void *(*)(void *), void *)
`new` a `Generator` - malloc it and initialise it.
Generator *gen = Generator_New(mystart, &params);
// Generator is used
// ... later:
Generator_Delete(gen);
### void Generator_dtor(Generator *gen)
Finalise a `Generator`. Once a `Generator` is no longer needed, it must be finalised:
// earlier...
Generator gen;
Generator_ctor(&gen, mystart, &params);
// Generator is used
// the Generator is no longer needed
Generator_dtor(&gen);
### void Generator_Delete(Generator *)
Finalise then `free()` a `Generator`. Once a `new`ed `Generator` is no longer needed, it must be deleted:
// earlier...
Generator *gen = Generator_New(mystart, &params);
// Generator is used
// the Generator is no longer needed
Generator_Delete(gen);
### bool Generator_Next(Generator *, void **value)
Get the next value yielded by the `Generator`.
void *value;
while(Generator_Next(gen, &value)){
// use value here
}
When `true` is returned, `value` is the value yielded by the `Generator`. When `false` is returned, `value` is the value returned when the `Generator` returned - the `Generator` has finished.
### bool Generator_Yield(void *)
Yield a value from a `Generator` function.
bool domore = Generator_Yield(value);
`value` is then provided by `Generator_Next()` as the next value from the generator. The `bool` returned by `Generator_Yield()` indicates whether more values should be provided by your generator function. When `true` provide more values if possible. When `false` close files, free memory, free up any other resources and `return`. `false` is returned when the `Generator` is being finalised.
## Coroutine
### void Coroutine_StartSystem();
Start the coroutine system on this thread. When you've finished with `Coroutine` call `Coroutine_Stop()`. `Coroutine` can be started & stopped many times on one thread. The total stack allowed for all coroutines running on a thread is the size of the call stack on that thread.
### void Coroutine_StopSystem();
Stop the coroutine system on this thread.
### Coroutine_Start
void *(*)(void *param)
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()`.
### Coroutine *Coroutine_New(Coroutine_Start start)
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.
### void Coroutine_Run_Coroutine(Coroutine *cor, void *value)
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.
### void *Coroutine_Run(Coroutine_Start start, void *value)
Convenience wrapper for `Coroutine_Run_Coroutine` which creates the `Coroutine` and retrieves its result.
### void Coroutine_Delete(Coroutine *cor)
Use `Coroutine_Delete()` to delete a coroutine when it is no longer needed.
### void Coroutine_Continue(Coroutine *cor, void *value, bool early)
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 is added to the head of tail of the list of runable coroutines.
### void *Coroutine_Yield(void *value, Coroutine_YieldCallback on_yield, void *this)
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()`.
### void *Coroutine_GetValue(Coroutine *cor)
Return the `Coroutine`'s value - the value last yielded, or returned by its `start` routine.
### Coroutine *Coroutine_GetActive()
Return whihc coroutine is currently running, ie the caller's `Coroutine`.
### bool Coroutine_IsRunning(Coroutine *cor)
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.
### int Coroutine_GetStackHeadroom()
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.
### bool Coroutine_HasCoroutinesInFreePool()
Return whether the coroutine system has any coroutine stacks available in its coroutine stack free pool.
### void *Coroutine_GetCStackTop()
Return an address which is near to the top of used C stack.
### void *Coroutine_Chain(Coroutine_Start start, void *value)
Run `start` with `value` on a new coroutine, and return its return value. It is expected that `Coroutine_Chain()` will be used when your coroutine is running short of stack - it is not an alternative to `Coroutine_Run()`.
coroutine/async.c
26
27
1
1
coroutine/coroutine.c
26
27
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
#include "coroutine.h"
#include <assert.h>
#include <setjmp.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include "cor_thread_local.h"
static void *mustmalloc(size_t size){
void *p = malloc(size);
assert(p);
return p;
}
#define New(type, ...) (type##_ctor((type *)mustmalloc(sizeof(type), ## __VA_ARGS__)))
#define Delete(ptr, type) ((ptr) ? (type##_dtor(ptr), free(ptr), (ptr) = NULL) : (void)0)
///////////////////////////////////////////////////////////////////////////////
// 2-way linked lists...
//
// Brought inline here to avoid namespace polution
///////////////////////////////////////////////////////////////////////////////
typedef struct List_Link List_Link;
struct List_Link {
List_Link *next;
List_Link *prev;
};
typedef struct List_Head List_Head;
struct List_Head {
union {
struct {
List_Link link;
List_Link *filler;
} fwd;
struct {
List_Link *filler;
List_Link link;
} back;
};
};
static inline bool List_IsEmpty(
const List_Head *list
){
return list->fwd.link.next == &list->back.link;
}
static inline List_Link *List_GetHead(
const List_Head *list
){
return List_IsEmpty(list) ? NULL : list->fwd.link.next;
}
// static inline List_Link *List_GetTail(
// const List_Head *list
// ){
// return List_IsEmpty(list) ? NULL : list->back.link.prev;
// }
#define OFFSETOF(Container, Field) ((char *)&((Container *)4)->Field - (char *)(Container *)4)
#define List_Link_Container(Container, Link, link) ((Container *)((char *)(link) - OFFSETOF(Container, Link)))
static inline void List_Init(
List_Head *list
){
list->fwd.link.next = &list->back.link;
list->fwd.link.prev = NULL;
list->back.link.prev = &list->fwd.link;
}
static inline void List_AddHead(
List_Head *list,
List_Link *link
){
List_Link *first = list->fwd.link.next;
link->next = first;
link->prev = &list->fwd.link;
first->prev = link;
list->fwd.link.next = link;
}
static inline void List_AddTail(
List_Head *list,
List_Link *link
){
List_Link *last = list->back.link.prev;
link->prev = last;
link->next = &list->back.link;
last->next = link;
list->back.link.prev = link;
}
static inline void List_Remove(
List_Link *link
){
link->prev->next = link->next;
link->next->prev = link->prev;
}
///////////////////////////////////////////////////////////////////////////////
// ...2-way linked lists
///////////////////////////////////////////////////////////////////////////////
typedef struct Coroutines Coroutines;
enum {
Coroutines_Idle,
Coroutines_Starting,
Coroutines_Started,
Coroutines_Active,
Coroutines_Stopping
};
enum {
Chunk_Initial,
Chunk_Create,
Chunk_Enter
};
enum {
Coroutine_Constructing,
Coroutine_Free,
Coroutine_Idle,
Coroutine_Running,
Coroutine_Waiting,
Coroutine_Complete
};
enum {
Coroutines_Init,
Coroutines_AllocatedChunk,
Coroutines_CoroutineComplete,
};
struct Coroutine {
Coroutines *coroutines;
List_Link link;
jmp_buf buf;
void *this;
Coroutine_Start start;
void *entry_param;
void *value;
char state;
char action;
};
struct Coroutines {
pthread_mutex_t mutex;
jmp_buf controller;
jmp_buf chunk_allocated;
// singletons
Coroutine *tip; // top of stack chunk
Coroutine *active; // currently running coroutine
Coroutine *primary; // Coroutine_Run coroutine
// lists
List_Head free;
List_Head inactive; // idle or complete
List_Head runable; // running or waiting to run
List_Head waiting; // yielded / waiting to run
pthread_cond_t waiting_cond;
// state
char state;
};
_Cor_thread_local Coroutines *g_c;
static void stack_chunk_chunk(Coroutine *parent);
static void stack_chunk_base(Coroutine *parent);
static void Coroutine_PrimeStackChunks()
{
unsigned char chunk_of_stack[COROUTINE_STACK_SIZE];
chunk_of_stack[0] = 0xde;
chunk_of_stack[1] = 0xad;
chunk_of_stack[2] = 0xbe;
chunk_of_stack[3] = 0xef;
chunk_of_stack[COROUTINE_STACK_SIZE - 4] = 0xde;
chunk_of_stack[COROUTINE_STACK_SIZE - 3] = 0xad;
chunk_of_stack[COROUTINE_STACK_SIZE - 2] = 0xbe;
chunk_of_stack[COROUTINE_STACK_SIZE - 1] = 0xef;
stack_chunk_base(NULL);
}
static void stack_chunk_chunk(
Coroutine *parent
){
unsigned char chunk_of_stack[COROUTINE_STACK_SIZE];
chunk_of_stack[0] = 0xde;
chunk_of_stack[1] = 0xad;
chunk_of_stack[2] = 0xbe;
chunk_of_stack[3] = 0xef;
chunk_of_stack[COROUTINE_STACK_SIZE - 4] = 0xde;
chunk_of_stack[COROUTINE_STACK_SIZE - 3] = 0xad;
chunk_of_stack[COROUTINE_STACK_SIZE - 2] = 0xbe;
chunk_of_stack[COROUTINE_STACK_SIZE - 1] = 0xef;
stack_chunk_base(parent);
}
static void Coroutine_RunNext()
{
// arrive here with mutex unlocked
int r;
r = pthread_mutex_lock(&g_c->mutex);
assert(r == 0);
while (List_IsEmpty(&g_c->runable)) {
r = pthread_cond_wait(&g_c->waiting_cond, &g_c->mutex);
assert(r == 0);
}
Coroutine *next = List_Link_Container(Coroutine, link, List_GetHead(&g_c->runable));
assert(next->state == Coroutine_Running);
longjmp(next->buf, Chunk_Enter);
assert(false);
}
static void stack_chunk_base(
Coroutine *parent
){
Coroutine here;
here.state = Coroutine_Constructing;
switch (setjmp(here.buf)) {
case Chunk_Initial:
// got here for the first time
// parent now has a chunk_of_stack - add it to the free list
if (parent) {
assert(parent->state == Coroutine_Constructing);
parent->state = Coroutine_Free;
List_AddHead(&g_c->free, &parent->link);
}
// note that here is the tip of the chunk-claim stack
here.coroutines = g_c;
g_c->tip = &here;
// return to the coroutine allocator
longjmp(g_c->chunk_allocated, 1);
case Chunk_Create:
// request to create a new chunk on the stack
assert(here.state == Coroutine_Constructing);
stack_chunk_chunk(&here);
assert(false);
case Chunk_Enter:
// request to start a coroutine (ie use the chunk for a coroutine)
// arrive here with mutex locked
assert(here.state == Coroutine_Running);
g_c->active = &here;
int r = pthread_mutex_unlock(&g_c->mutex);
assert(r == 0);
here.value = here.start(here.entry_param);
r = pthread_mutex_lock(&g_c->mutex);
assert(r == 0);
g_c->active = NULL;
assert(here.state == Coroutine_Running);
List_Remove(&here.link);
here.state = Coroutine_Complete;
List_AddTail(&g_c->inactive, &here.link);
// coroutine has completed
if (g_c->primary == &here) {
// if primary coroutine - return to Coroutine_Run
longjmp(g_c->controller, Coroutines_CoroutineComplete);
}
r = pthread_mutex_unlock(&g_c->mutex);
assert(r == 0);
Coroutine_RunNext();
assert(false);
}
}
void Coroutine_StartSystem()
{
assert(!g_c);
g_c = mustmalloc(sizeof(Coroutines));
g_c->state = Coroutines_Starting;
pthread_mutexattr_t attr;
int r = pthread_mutexattr_init(&attr);
assert(r == 0);
r = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
assert(r == 0);
r = pthread_mutex_init(&g_c->mutex, &attr);
assert(r == 0);
r = pthread_mutexattr_destroy(&attr);
assert(r == 0);
g_c->tip = NULL;
g_c->active = NULL;
List_Init(&g_c->free);
List_Init(&g_c->inactive);
List_Init(&g_c->runable);
List_Init(&g_c->waiting);
r = pthread_cond_init(&g_c->waiting_cond, NULL);
assert(r == 0);
// prime the chunk system
if (!setjmp(g_c->chunk_allocated)){
Coroutine_PrimeStackChunks();
assert(false);
}
assert(g_c->state == Coroutines_Starting);
g_c->state = Coroutines_Started;
}
void Coroutine_StopSystem()
{
int r = pthread_mutex_lock(&g_c->mutex);
assert(r == 0);
assert(g_c->state == Coroutines_Started);
g_c->state = Coroutines_Stopping;
assert(List_IsEmpty(&g_c->inactive));
r = pthread_cond_destroy(&g_c->waiting_cond);
assert(r == 0);
assert(g_c->state == Coroutines_Stopping);
pthread_mutex_unlock(&g_c->mutex);
assert(r == 0);
g_c->state = Coroutines_Idle;
r = pthread_mutex_destroy(&g_c->mutex);
assert(r == 0);
free(g_c);
g_c = NULL;
}
void Coroutine_Run_Coroutine(
Coroutine *cor,
void *value
){
Coroutines *cors = cor->coroutines;
assert(g_c == cors);
int r = pthread_mutex_lock(&cors->mutex);
assert(r == 0);
assert(cors->state == Coroutines_Started);
cors->state = Coroutines_Active;
cors->primary = cor;
Coroutine_Continue(cor, value, true);
if (!setjmp(cors->controller)){
pthread_mutex_unlock(&cors->mutex);
assert(r == 0);
// start the first coroutine
Coroutine_RunNext();
}
// arrive here with mutex locked
assert(List_IsEmpty(&cors->runable));
assert(List_IsEmpty(&cors->waiting));
assert(cors->state == Coroutines_Active);
cors->state = Coroutines_Started;
pthread_mutex_unlock(&cors->mutex);
assert(r == 0);
}
void *Coroutine_Run(
Coroutine_Start start,
void *value
){
Coroutine *cor = Coroutine_New(start);
Coroutine_Run_Coroutine(cor, value);
void *res = Coroutine_GetValue(cor);
Coroutine_Delete(cor);
return res;
}
Coroutine *Coroutine_New(
Coroutine_Start start
){
assert((g_c->state == Coroutines_Started && List_IsEmpty(&g_c->inactive)) || g_c->state == Coroutines_Active);
// if none free - add one
if (List_IsEmpty(&g_c->free)){
if (!setjmp(g_c->chunk_allocated)){
longjmp(g_c->tip->buf, Chunk_Create);
}
}
Coroutine *cor = List_Link_Container(Coroutine, link, List_GetHead(&g_c->free));
assert(cor->state == Coroutine_Free);
cor->state = Coroutine_Idle;
cor->start = start;
cor->value = NULL;
List_Remove(&cor->link);
List_AddHead(&g_c->inactive, &cor->link);
return cor;
}
void Coroutine_Delete(
Coroutine *cor
){
Coroutines *cors = cor->coroutines;
int r = pthread_mutex_lock(&cors->mutex);
assert(r == 0);
assert(cor->state == Coroutine_Idle || cor->state == Coroutine_Complete);
cor->state = Coroutine_Free;
List_Remove(&cor->link);
List_AddTail(&cors->free, &cor->link);
r = pthread_mutex_unlock(&cors->mutex);
assert(r == 0);
}
void Coroutine_Continue(
Coroutine *cor,
void *value,
bool early
){
Coroutines *cors = cor->coroutines;
int r = pthread_mutex_lock(&cors->mutex);
assert(r == 0);
assert(cor->state == Coroutine_Idle || cor->state == Coroutine_Waiting);
cor->entry_param = value;
cor->state = Coroutine_Running;
List_Remove(&cor->link);
if ( early ) {
List_AddHead(&cors->runable, &cor->link);
} else {
List_AddTail(&cors->runable, &cor->link);
}
r = pthread_cond_signal(&cors->waiting_cond);
r = pthread_mutex_unlock(&cors->mutex);
assert(r == 0);
}
void *Coroutine_Yield(
void *value,
Coroutine_YieldCallback on_yield,
void *this
){
int r = pthread_mutex_lock(&g_c->mutex);
assert(r == 0);
Coroutine *me = g_c->active;
Coroutines *cors = me->coroutines;
assert(me && me->state == Coroutine_Running && cors == g_c);
me->value = value;
me->state = Coroutine_Waiting;
List_Remove(&me->link);
List_AddTail(&cors->waiting, &me->link);
switch (setjmp(me->buf)){
case Chunk_Initial:
r = pthread_mutex_unlock(&cors->mutex);
assert(r == 0);
on_yield(this);
Coroutine_RunNext();
case Chunk_Create:
assert(false);
case Chunk_Enter:
// arrive here with mutex locked
cors->active = me;
// when we return here - we are running again
assert(me->state == Coroutine_Running);
void *res = me->entry_param;
r = pthread_mutex_unlock(&cors->mutex);
assert(r == 0);
return res;
}
return NULL;
}
void *Coroutine_GetValue(
Coroutine *cor
){
return cor->value;
}
Coroutine *Coroutine_GetActive()
{
return g_c->active;
}
bool Coroutine_IsRunning(
Coroutine *cor
)
{
int state = cor->state;
return state == Coroutine_Running || state == Coroutine_Waiting;
}
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
#include "coroutine.h"
#include <assert.h>
#include <setjmp.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include "cor_thread_local.h"
static void *mustmalloc(size_t size){
void *p = malloc(size);
assert(p);
return p;
}
#define New(type, ...) (type##_ctor((type *)mustmalloc(sizeof(type), ## __VA_ARGS__)))
#define Delete(ptr, type) ((ptr) ? (type##_dtor(ptr), free(ptr), (ptr) = NULL) : (void)0)
static void Coroutine_RunNext();
///////////////////////////////////////////////////////////////////////////////
// 2-way linked lists...
//
// Brought inline here to avoid namespace polution
///////////////////////////////////////////////////////////////////////////////
typedef struct List_Link List_Link;
struct List_Link {
List_Link *next;
List_Link *prev;
};
typedef struct List_Head List_Head;
struct List_Head {
union {
struct {
List_Link link;
List_Link *filler;
} fwd;
struct {
List_Link *filler;
List_Link link;
} back;
};
};
static inline bool List_IsEmpty(
const List_Head *list
){
return list->fwd.link.next == &list->back.link;
}
static inline List_Link *List_GetHead(
const List_Head *list
){
return List_IsEmpty(list) ? NULL : list->fwd.link.next;
}
// static inline List_Link *List_GetTail(
// const List_Head *list
// ){
// return List_IsEmpty(list) ? NULL : list->back.link.prev;
// }
#define OFFSETOF(Container, Field) ((char *)&((Container *)4)->Field - (char *)(Container *)4)
#define List_Link_Container(Container, Link, link) ((Container *)((char *)(link) - OFFSETOF(Container, Link)))
static inline void List_Init(
List_Head *list
){
list->fwd.link.next = &list->back.link;
list->fwd.link.prev = NULL;
list->back.link.prev = &list->fwd.link;
}
static inline void List_AddHead(
List_Head *list,
List_Link *link
){
List_Link *first = list->fwd.link.next;
link->next = first;
link->prev = &list->fwd.link;
first->prev = link;
list->fwd.link.next = link;
}
static inline void List_AddTail(
List_Head *list,
List_Link *link
){
List_Link *last = list->back.link.prev;
link->prev = last;
link->next = &list->back.link;
last->next = link;
list->back.link.prev = link;
}
static inline void List_Remove(
List_Link *link
){
link->prev->next = link->next;
link->next->prev = link->prev;
}
///////////////////////////////////////////////////////////////////////////////
// ...2-way linked lists
///////////////////////////////////////////////////////////////////////////////
typedef struct Coroutines Coroutines;
enum {
Coroutines_Idle,
Coroutines_Starting,
Coroutines_Started,
Coroutines_Active,
Coroutines_Stopping
};
enum {
Chunk_Initial,
Chunk_Create,
Chunk_Enter
};
typedef enum Coroutine_State {
Coroutine_Constructing,
Coroutine_Free,
Coroutine_Idle,
Coroutine_Running,
Coroutine_Waiting,
Coroutine_Complete
} Coroutine_State;
enum {
Coroutines_Init,
Coroutines_AllocatedChunk,
Coroutines_CoroutineComplete,
};
struct Coroutine {
Coroutines *coroutines; // so can work with it off-thread
List_Link link; // for whichever list it's on
jmp_buf buf; // how to get back to it
unsigned char *guard; // where the stack overrun guard is
Coroutine_Start start; // entry point
void *entry_param; // to pass to start
void *value; // yielded/returned
Coroutine_State state;
};
struct Coroutines {
pthread_mutex_t mutex;
jmp_buf controller; // to return from Coroutine_Run
jmp_buf chunk_allocated;// for chunk allocation
unsigned char *guard; // the stack guard for the startup sequence
// singletons
Coroutine *tip; // top of stack chunk
Coroutine *active; // currently running coroutine
Coroutine *primary; // Coroutine_Run coroutine
// lists
List_Head free;
List_Head inactive; // idle or complete
List_Head runable; // running or waiting to run
List_Head waiting; // yielded / waiting to run
pthread_cond_t waiting_cond;
// Summary of the system
Coroutine_Report report;
// state
char state;
};
_Cor_thread_local Coroutines *g_c;
static void stack_chunk_chunk(Coroutine *parent);
static void stack_chunk_base(Coroutine *parent, unsigned char *guard);
// Check whether the guard is intact
static inline bool Check_Guard(
unsigned char *guard
){
return guard[0] == 0xde &&
guard[1] == 0xad &&
guard[2] == 0xbe &&
guard[3] == 0xef;
}
static void Coroutine_PrimeStackChunks()
{
unsigned char chunk_of_stack[COROUTINE_STARTUP_STACK_SIZE];
for (int i = 0; i < COROUTINE_STARTUP_STACK_SIZE-3; i += 4){
chunk_of_stack[i+0] = 0xde;
chunk_of_stack[i+1] = 0xad;
chunk_of_stack[i+2] = 0xbe;
chunk_of_stack[i+3] = 0xef;
}
assert(Check_Guard(chunk_of_stack));
// Stacks grow down in memory (almost always), so if the caller of this function changes
// the guard before entering the coroutine system, it has overrun the startup stack
g_c->guard = chunk_of_stack;
stack_chunk_base(NULL, NULL);
}
static void stack_chunk_chunk(
Coroutine *parent
){
unsigned char chunk_of_stack[COROUTINE_STACK_SIZE];
for (int i = 0; i < COROUTINE_STACK_SIZE-3; i += 4){
chunk_of_stack[i+0] = 0xde;
chunk_of_stack[i+1] = 0xad;
chunk_of_stack[i+2] = 0xbe;
chunk_of_stack[i+3] = 0xef;
}
stack_chunk_base(parent, chunk_of_stack);
}
static void stack_chunk_base(
Coroutine *parent,
unsigned char *guard
){
Coroutine here;
here.state = Coroutine_Constructing;
switch (setjmp(here.buf)) {
case Chunk_Initial:
// got here for the first time
// parent now has a chunk_of_stack - add it to the free list
if (parent) {
assert(parent->state == Coroutine_Constructing);
assert(Check_Guard(guard));
parent->guard = guard;
parent->state = Coroutine_Free;
List_AddHead(&g_c->free, &parent->link);
g_c->report.coroutines_pool_size += 1;
}
// note that here is the tip of the chunk-claim stack
here.coroutines = g_c;
g_c->tip = &here;
// return to the coroutine allocator
longjmp(g_c->chunk_allocated, 1);
case Chunk_Create:
// request to create a new chunk on the stack
assert(here.state == Coroutine_Constructing);
stack_chunk_chunk(&here);
assert(false);
case Chunk_Enter:
// request to start a coroutine (ie use the chunk for a coroutine)
// arrive here with mutex locked
assert(here.state == Coroutine_Running);
g_c->active = &here;
int r = pthread_mutex_unlock(&g_c->mutex);
assert(r == 0);
here.value = here.start(here.entry_param);
// check the guard
if (!Check_Guard(here.guard)){
printf("Coroutine has overrun its stack - checked after returning from coroutine function\n");
exit(EXIT_FAILURE);
}
r = pthread_mutex_lock(&g_c->mutex);
assert(r == 0);
g_c->active = NULL;
assert(here.state == Coroutine_Running);
List_Remove(&here.link);
here.state = Coroutine_Complete;
List_AddTail(&g_c->inactive, &here.link);
// coroutine has completed
if (g_c->primary == &here) {
// if primary coroutine - return to Coroutine_Run
longjmp(g_c->controller, Coroutines_CoroutineComplete);
}
r = pthread_mutex_unlock(&g_c->mutex);
assert(r == 0);
Coroutine_RunNext();
assert(false);
}
}
static void Coroutine_RunNext()
{
// arrive here with mutex unlocked
int r;
r = pthread_mutex_lock(&g_c->mutex);
assert(r == 0);
while (List_IsEmpty(&g_c->runable)) {
r = pthread_cond_wait(&g_c->waiting_cond, &g_c->mutex);
assert(r == 0);
}
Coroutine *next = List_Link_Container(Coroutine, link, List_GetHead(&g_c->runable));
assert(next->state == Coroutine_Running);
longjmp(next->buf, Chunk_Enter);
assert(false);
}
void Coroutine_StartSystem()
{
assert(!g_c);
g_c = mustmalloc(sizeof(Coroutines));
g_c->state = Coroutines_Starting;
pthread_mutexattr_t attr;
int r = pthread_mutexattr_init(&attr);
assert(r == 0);
r = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
assert(r == 0);
r = pthread_mutex_init(&g_c->mutex, &attr);
assert(r == 0);
r = pthread_mutexattr_destroy(&attr);
assert(r == 0);
g_c->tip = NULL;
g_c->active = NULL;
List_Init(&g_c->free);
List_Init(&g_c->inactive);
List_Init(&g_c->runable);
List_Init(&g_c->waiting);
r = pthread_cond_init(&g_c->waiting_cond, NULL);
assert(r == 0);
g_c->report.coroutines_created = 0;
g_c->report.coroutines_pool_size = 0;
g_c->report.lowest_headroom = COROUTINE_STACK_SIZE;
// prime the chunk system
if (!setjmp(g_c->chunk_allocated)){
Coroutine_PrimeStackChunks();
assert(false);
}
assert(g_c->state == Coroutines_Starting);
g_c->state = Coroutines_Started;
}
Coroutine_Report Coroutine_StopSystem()
{
int r = pthread_mutex_lock(&g_c->mutex);
assert(r == 0);
assert(g_c->state == Coroutines_Started);
g_c->state = Coroutines_Stopping;
int stackminheadroom = COROUTINE_STACK_SIZE;
for (List_Link *link = g_c->free.fwd.link.next; link->next; link = link->next){
Coroutine *cor = List_Link_Container(Coroutine, link, link);
for (int i = 4; i < COROUTINE_STACK_SIZE-3; i += 4){
if (!Check_Guard(&cor->guard[i])){
stackminheadroom = i < stackminheadroom ? i : stackminheadroom;
break;
}
}
}
g_c->report.lowest_headroom = stackminheadroom;
assert(List_IsEmpty(&g_c->inactive));
r = pthread_cond_destroy(&g_c->waiting_cond);
assert(r == 0);
assert(g_c->state == Coroutines_Stopping);
pthread_mutex_unlock(&g_c->mutex);
assert(r == 0);
g_c->state = Coroutines_Idle;
r = pthread_mutex_destroy(&g_c->mutex);
assert(r == 0);
Coroutine_Report res = g_c->report;
free(g_c);
g_c = NULL;
return res;
}
void Coroutine_Run_Coroutine(
Coroutine *cor,
void *value
){
Coroutines *cors = cor->coroutines;
assert(g_c == cors);
int r = pthread_mutex_lock(&cors->mutex);
assert(r == 0);
assert(cors->state == Coroutines_Started);
cors->state = Coroutines_Active;
cors->primary = cor;
Coroutine_Continue(cor, value, true);
if (!setjmp(cors->controller)){
pthread_mutex_unlock(&cors->mutex);
assert(r == 0);
// check the guard
if (!Check_Guard(g_c->guard)){
printf("Coroutine startup stack as has overrun - checked on entering the main coroutine\n");
exit(EXIT_FAILURE);
}
// start the first coroutine
Coroutine_RunNext();
}
// arrive here with mutex locked
assert(List_IsEmpty(&cors->runable));
assert(List_IsEmpty(&cors->waiting));
assert(cors->state == Coroutines_Active);
cors->state = Coroutines_Started;
pthread_mutex_unlock(&cors->mutex);
assert(r == 0);
}
void *Coroutine_Run(
Coroutine_Start start,
void *value
){
Coroutine *cor = Coroutine_New(start);
Coroutine_Run_Coroutine(cor, value);
void *res = Coroutine_GetValue(cor);
Coroutine_Delete(cor);
return res;
}
Coroutine *Coroutine_New(
Coroutine_Start start
){
assert((g_c->state == Coroutines_Started && List_IsEmpty(&g_c->inactive)) || g_c->state == Coroutines_Active);
// if none free - add one
if (List_IsEmpty(&g_c->free)){
if (!setjmp(g_c->chunk_allocated)){
longjmp(g_c->tip->buf, Chunk_Create);
}
}
Coroutine *cor = List_Link_Container(Coroutine, link, List_GetHead(&g_c->free));
assert(cor->state == Coroutine_Free);
cor->state = Coroutine_Idle;
cor->start = start;
cor->value = NULL;
List_Remove(&cor->link);
List_AddHead(&g_c->inactive, &cor->link);
g_c->report.coroutines_created += 1;
return cor;
}
void Coroutine_Delete(
Coroutine *cor
){
Coroutines *cors = cor->coroutines;
int r = pthread_mutex_lock(&cors->mutex);
assert(r == 0);
assert(cor->state == Coroutine_Idle || cor->state == Coroutine_Complete);
cor->state = Coroutine_Free;
List_Remove(&cor->link);
List_AddTail(&cors->free, &cor->link);
r = pthread_mutex_unlock(&cors->mutex);
assert(r == 0);
}
void Coroutine_Continue(
Coroutine *cor,
void *value,
bool early
){
Coroutines *cors = cor->coroutines;
int r = pthread_mutex_lock(&cors->mutex);
assert(r == 0);
assert(cor->state == Coroutine_Idle || cor->state == Coroutine_Waiting);
cor->entry_param = value;
cor->state = Coroutine_Running;
List_Remove(&cor->link);
if ( early ) {
List_AddHead(&cors->runable, &cor->link);
} else {
List_AddTail(&cors->runable, &cor->link);
}
r = pthread_cond_signal(&cors->waiting_cond);
r = pthread_mutex_unlock(&cors->mutex);
assert(r == 0);
}
void *Coroutine_Yield(
void *value,
Coroutine_YieldCallback on_yield,
void *yield_me
){
Coroutine *me = g_c->active;
if (!Check_Guard(me->guard)){
printf("Coroutine has overrun its stack - checked when yielding coroutine\n");
exit(EXIT_FAILURE);
}
int r = pthread_mutex_lock(&g_c->mutex);
assert(r == 0);
Coroutines *cors = me->coroutines;
assert(me && me->state == Coroutine_Running && cors == g_c);
me->value = value;
me->state = Coroutine_Waiting;
List_Remove(&me->link);
List_AddTail(&cors->waiting, &me->link);
switch (setjmp(me->buf)){
case Chunk_Initial:
r = pthread_mutex_unlock(&cors->mutex);
assert(r == 0);
on_yield(yield_me);
Coroutine_RunNext();
case Chunk_Create:
assert(false);
case Chunk_Enter:
// arrive here with mutex locked
cors->active = me;
// when we return here - we are running again
assert(me->state == Coroutine_Running);
void *res = me->entry_param;
r = pthread_mutex_unlock(&cors->mutex);
assert(r == 0);
return res;
}
return NULL;
}
void *Coroutine_GetValue(
Coroutine *cor
){
return cor->value;
}
Coroutine *Coroutine_GetActive()
{
return g_c->active;
}
int Coroutine_GetStackHeadroom(){
unsigned char tbuf[4];
return tbuf - g_c->active->guard - 4;
}
bool Coroutine_HasCoroutinesInFreePool(){
return !List_IsEmpty(&g_c->free);
}
void *Coroutine_GetCStackTop(){
return g_c->tip;
}
struct Coroutine_ChainParam {
Coroutine_Start start;
void *value;
Coroutine *ret;
};
static void *Coroutine_ChainFn(
void *param
){
struct Coroutine_ChainParam *params = (struct Coroutine_ChainParam *)param;
Coroutine_Continue(params->ret, params->start(params->value), true);
return NULL;
}
static void Coroutine_ChainYield(
void *unused
){
(void)unused;
}
void *Coroutine_Chain(
Coroutine_Start start,
void *value
){
if (!Check_Guard(Coroutine_GetActive()->guard)){
printf("Coroutine has overrun its stack - checked when chaining\n");
exit(EXIT_FAILURE);
}
Coroutine *cor = Coroutine_New(Coroutine_ChainFn);
struct Coroutine_ChainParam params = {
start,
value,
Coroutine_GetActive()
};
Coroutine_Continue(cor, &params, true);
void *res = Coroutine_Yield(NULL, Coroutine_ChainYield, NULL);
Coroutine_Delete(cor);
return res;
}
bool Coroutine_IsRunning(
Coroutine *cor
)
{
int state = cor->state;
return state == Coroutine_Running || state == Coroutine_Waiting;
}
examples/main.c
26
27
1
1
include/async.def.h
26
27
1
1
include/async.h
26
27
1
1
include/coroutine.h
26
27
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#ifndef COROUTINE_H
#define COROUTINE_H
#include <stdbool.h>
///////////////////////////////////////////////////////////////////////////////
// Coroutine
//
// Coroutines for C, based on setjmp/longjmp.
// Thread safe - each thread has its own coroutine system
// Coroutines are cooperatively scheduled
// Coroutines have their own stack (currently 16K each)
// A coroutine can be continued, queried, or deleted on a different thread.
//
// Usage:
// Coroutine_StartSystem(); // call once per thread before using coroutines
// Coroutine *co = Coroutine_New(start_function);
// void *result = Coroutine_Run(co, initial_value);
// Coroutine_Delete(co);
// Coroutine_StopSystem(); // call once per thread when done with coroutines
//
// Inside the coroutine function:
// void *value = Coroutine_Yield(yield_value, on_yield, this);
// ...
// return return_value;
//
// To create a coroutine:
// Coroutine *co = Coroutine_New(start_function);
// To start or continue a coroutine:
// void *result = Coroutine_Continue(co, value, early);
// // early=true puts the coroutine at the head of the run queue
// // early=false puts the coroutine at the tail of the run queue
// To yield from inside a coroutine:
// void *value = Coroutine_Yield(yield_value, on_yield, this);
// // on_yield is called before the next coroutine is run
// // 'this' is passed to on_yield as its parameter
// // value is the value passed to Coroutine_Continue
// To delete a coroutine:
// Coroutine_Delete(co);
// To get the value yielded from, or returned by a corotuine:
// void *value = Coroutine_GetValue(co);
// To get the currently running coroutine (NULL if none):
// Coroutine *co = Coroutine_GetActive();
// To check if a coroutine is currently running:
// bool running = Coroutine_IsRunning(co);
//
// Notes:
// Coroutine is not expected to be used directly, but as a foundation for
// higher level constructs such as Generators, Async, etc.
//
///////////////////////////////////////////////////////////////////////////////
// The stack is used as follows:
// +------------------+ <- stack top
// | coroutine header | <- more claimed as needed in Coroutine_New
// +------------------+ <-
// | coroutine stack | <-
// +------------------+ <-
// | coroutine header |
// +------------------+
// | coroutine stack |
// +------------------+
// | coroutine header |
// +------------------+
// | coroutine stack |
// +------------------+
// | coroutine header |
// +------------------+
// | coroutine stack |
// +------------------+
// | coroutine header |
// +------------------+
// | startup space | <- set aside by Coroutine_StartSystem
// +------------------+
// | caller | <- This calls Coroutine_StartSystem etc
// +------------------+
// | used stack |
// +------------------+ <- stack bottom
// Each coroutine has this much stack:
#define COROUTINE_STACK_SIZE 16384
// When Coroutines is started, an amount of stack is set aside to give
// the caller of Coroutine_StartSystem a bit of room to work before calling
// Coroutine_Run(), that is this amount:
#define COROUTINE_STARTUP_STACK_SIZE 1024
typedef struct Coroutine Coroutine;
typedef void (*Coroutine_YieldCallback)(void *this);
typedef void *(*Coroutine_Start)(void *);
void Coroutine_StartSystem();
void Coroutine_StopSystem();
Coroutine *Coroutine_New(Coroutine_Start start);
void Coroutine_Run_Coroutine(Coroutine *cor, void *value);
void *Coroutine_Run(Coroutine_Start start, void *value);
void Coroutine_Delete(Coroutine *cor);
void Coroutine_Continue(Coroutine *cor, void *value, bool early);
void *Coroutine_Yield(void *value, Coroutine_YieldCallback on_yield, void *me);
void *Coroutine_GetValue(Coroutine *cor);
Coroutine *Coroutine_GetActive();
bool Coroutine_IsRunning(Coroutine *cor);
#endif
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#ifndef COROUTINE_H
#define COROUTINE_H
#include <stdbool.h>
///////////////////////////////////////////////////////////////////////////////
// Coroutine
//
// Coroutines for C, based on setjmp/longjmp.
// Thread safe - each thread has its own coroutine system
// Coroutines are cooperatively scheduled
// Coroutines have their own stack (currently 16K each)
// A coroutine can be continued, queried, or deleted on a different thread.
//
// Usage:
// Coroutine_StartSystem(); // call once per thread before using coroutines
// Coroutine *co = Coroutine_New(start_function);
// void *result = Coroutine_Run(co, initial_value);
// Coroutine_Delete(co);
// Coroutine_StopSystem(); // call once per thread when done with coroutines
//
// Inside the coroutine function:
// void *value = Coroutine_Yield(yield_value, on_yield, this);
// ...
// return return_value;
//
// To create a coroutine:
// Coroutine *co = Coroutine_New(start_function);
// To start or continue a coroutine:
// void *result = Coroutine_Continue(co, value, early);
// // early=true puts the coroutine at the head of the run queue
// // early=false puts the coroutine at the tail of the run queue
// To yield from inside a coroutine:
// void *value = Coroutine_Yield(yield_value, on_yield, this);
// // on_yield is called before the next coroutine is run
// // 'this' is passed to on_yield as its parameter
// // value is the value passed to Coroutine_Continue
// To delete a coroutine:
// Coroutine_Delete(co);
// To get the value yielded from, or returned by a corotuine:
// void *value = Coroutine_GetValue(co);
// To get the currently running coroutine (NULL if none):
// Coroutine *co = Coroutine_GetActive();
// To check if a coroutine is currently running:
// bool running = Coroutine_IsRunning(co);
//
// Notes:
// Coroutine is not expected to be used directly, but as a foundation for
// higher level constructs such as Generators, Async, etc.
//
///////////////////////////////////////////////////////////////////////////////
// The stack is used as follows:
// +------------------+ <- stack top
// | coroutine header | <- more claimed as needed in Coroutine_New
// +------------------+ <-
// | coroutine stack | <-
// +------------------+ <-
// | coroutine header |
// +------------------+
// | coroutine stack |
// +------------------+
// | coroutine header |
// +------------------+
// | coroutine stack |
// +------------------+
// | coroutine header |
// +------------------+
// | coroutine stack |
// +------------------+
// | coroutine header |
// +------------------+
// | startup space | <- set aside by Coroutine_StartSystem
// +------------------+
// | caller | <- This calls Coroutine_StartSystem etc
// +------------------+
// | used stack |
// +------------------+ <- stack bottom
// Each coroutine has this much stack:
#define COROUTINE_STACK_SIZE 65536
// When Coroutines is started, an amount of stack is set aside to give
// the caller of Coroutine_StartSystem a bit of room to work before calling
// Coroutine_Run(), that is this amount:
#define COROUTINE_STARTUP_STACK_SIZE 4096
// Returned by Coroutine_StopSystem(), this summarises the coroutine session
typedef struct Coroutine_Report {
unsigned coroutines_created;
unsigned coroutines_pool_size;
unsigned lowest_headroom;
} Coroutine_Report;
typedef struct Coroutine Coroutine;
typedef void (*Coroutine_YieldCallback)(void *this);
typedef void *(*Coroutine_Start)(void *);
void Coroutine_StartSystem();
Coroutine_Report Coroutine_StopSystem();
Coroutine *Coroutine_New(Coroutine_Start start);
void Coroutine_Run_Coroutine(Coroutine *cor, void *value);
void *Coroutine_Run(Coroutine_Start start, void *value);
void Coroutine_Delete(Coroutine *cor);
void Coroutine_Continue(Coroutine *cor, void *value, bool early);
void *Coroutine_Yield(void *value, Coroutine_YieldCallback on_yield, void *me);
void *Coroutine_GetValue(Coroutine *cor);
Coroutine *Coroutine_GetActive();
int Coroutine_GetStackHeadroom();
bool Coroutine_HasCoroutinesInFreePool();
void *Coroutine_GetCStackTop();
void *Coroutine_Chain(Coroutine_Start start, void *value);
bool Coroutine_IsRunning(Coroutine *cor);
#endif