2 contributors
780 lines25.3 KB
Newer
Older
-
+
commited
{line.log.rev}
on
8 months ago
1
Stackful coroutines in C.
8 months ago
17
2
8 months ago
3
* `Async` coroutines which can pause, waiting for a future.
4
* `Generator` coroutines used as generators for loops.
5
* `Coroutine` the coroutine engine used by `Async` and `Generator`.
8 months ago
17
6
8 months ago
7
Your code doesn't need to do anything special to be a coroutine, and only standard, or commonly available libraries are needed.
8 months ago
17
8
8 months ago
9
## Prerequisites
8 months ago
17
10
8 months ago
11
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.
8 months ago
17
12
8 months ago
13
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.
8 months ago
17
14
8 months ago
15
## Quick Start
8 months ago
17
16
8 months ago
17
### Async
8 months ago
17
18
8 months ago
19
To run an Async program:
8 months ago
17
20
8 months ago
21
#include "async.h"
22
main(){
23
Async_StartSystem();
24
void *res = NULL;
25
bool canceled = Async_Run(asyncmain, &param, &res);
26
Async_StopSystem();
27
}
28
29
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:
30
31
bool mytask(void *param, void **res){
32
33
// do your thing here
34
35
return canceled;
36
}
37
38
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.
39
40
Within your async task, create `Async_Task`s and `Async_Task_Await()` them when you want to wait for their result:
41
42
Async_Task task1;
43
Async_Task_ctor(&task1, adifferenttask, &task1param);
44
45
void *result;
46
bool canceled = Async_Task_Await(&task1, &result);
47
48
Async_Task_dtor(&task1);
49
50
// use the result
51
52
When a task needs to wait for something, and wants to allow other tasks to run, it should use a `Future`:
53
54
Async_Future future;
55
Async_Future_ctor(&future);
56
57
// pass the future to the background-thing-which-might-take-a-while
58
8 months ago
59
void *res;
60
bool canceled = Async_Future_Await(&future, &res);
8 months ago
61
62
Async_Future_dtor(&future);
63
64
When the background-thing-which-might-take-a-while has a result:
65
66
Async_Future_SetResult(future, false, result);
67
68
### Generators
69
70
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.
71
72
You will need a generator function:
73
74
void *yield_my_things(void *param){
75
bool domore = true;
76
77
// loop/call functions to find more values to yield, and when you have one:
78
domore = Generator_Yield(thing);
79
// .. if domore is false, exit your generator - it is being destructed
80
81
// not actually used by generators, but this is a useful convention for bubbling
82
// the flag out to calling functions.
83
return (void *)domore;
84
}
85
86
And to use it:
87
88
Generator gen;
89
Generator_ctor(&gen, yield_my_things, "..");
90
void *thing;
91
while(Generator_Next(&gen, &thing)){
92
// use thing - a value yielded by your generator
93
}
94
Generator_dtor(&gen);
95
96
### Coroutines
97
98
While you can use coroutines directly, it's designed as a system to support more useful patterns, like `Async` and `Generators`.
99
100
The Coroutines system must be started:
101
102
Coroutine_StartSystem();
103
// use coroutines here
104
Coroutine_StopSystem();
105
106
Your coroutine will need to have a start function:
107
108
void *start(void *param){
109
...
110
}
111
112
When there is no coroutine running, start your 'main' coroutine:
113
114
void *result = Coroutine_Run(comain, param);
115
116
Create other coroutines like this:
117
118
Coroutine *cor = Coroutine_New(start);
119
120
When you want a Coroutine to run, or to return from a yield:
121
122
Coroutine_Continue(cor, value, run_early);
123
124
`value` will be start function's parameter, or the value returned from the yield.
125
126
Within the Coroutine, to yield a value:
127
128
void *Coroutine_Yield(value, on_yield, void *me);
129
130
The on_yield function is called after the coroutine has been 'wait'ed, but before the next coroutine is resumed.
131
132
## How it Works
133
8 months ago
134
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 application, and whether your stack size is enough for the number of coroutines you might run.
8 months ago
135
8 months ago
136
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
137
138
## Style
139
8 months ago
17
140
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()`.
141
142
Something *oneofthem = Something_New();
143
// use oneofthem
144
Something_Delete(oneofthem);
145
146
Can be also be done like this, and this will run faster:
147
148
Something oneofthem;
149
Something_ctor(&oneofthem);
150
// use oneofthem
151
Something_dtor(&oneofthem);
152
8 months ago
153
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.
154
8 months ago
17
155
## Usage
156
157
When you are using coroutines or generators:
158
159
void *myfunc(void *){
160
// your function here
161
}
162
163
Coroutine_StartSystem();
164
Coroutine_Run(myfunc, (void *)myparam);
165
Coroutine_StopSystem();
166
167
If you also use async, then:
168
169
bool myfunc(void *myparam, void **res){
170
// your async function here
171
}
172
173
Async_StartSystem();
174
void *res = NULL;
175
bool canceled = Async_Run(myfunc, myparam, &res);
176
Async_StopSystem();
177
178
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.
179
8 months ago
180
## Stack Overruns
8 months ago
17
181
8 months ago
182
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:
8 months ago
17
183
8 months ago
184
* 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...
185
186
* 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...
187
188
* monitor stack headroom, and add another stack chunk if you need to:
189
190
In this last case you'll need to add some code at key points:
191
192
void *myfunction(void *param){
193
if (Coroutine_GetStackHeadroom() < MIN_ALLOWED_STACK){
194
return Coroutine_Chain(myfunction, param);
195
}
196
// do everything normally
197
}
198
199
More realistically:
200
201
struct myfunctionparams {
202
int a;
203
char *b;
204
struct dog *d;
205
}
206
207
void *mychain(void *param){
208
struct myfunctionparams *myparams = (struct myfunctionparams *)params;
209
return (void *)myfunction(myparams->a, myparams->b, *myparams->d);
210
}
211
212
int myfunction(int a, char *b, struct dog d){
213
if (Coroutine_GetStackHeadroom() < MIN_ALLOWED_STACK){
214
struct myfunctionparams params = {
215
a,
216
b,
217
&d
218
};
219
return (int)Coroutine_Chain(mychain, &params);
220
}
221
}
222
8 months ago
223
And if you want to panic if the C stack overruns:
224
225
if (Coroutine_GetStackHeadroom() < MIN_ALLOWED_COROUTINE_STACK){
226
if (Coroutine_HasCoroutinesInFreePool() ||
227
(char *)Coroutine_GetCStackTop() - c_stack_end >= MIN_ALLOWED_C_STACK) {
228
struct myfunctionparams params = {
229
a,
230
b,
231
&d
232
};
233
return (int)Coroutine_Chain(mychain, &params);
234
}
235
// panic now
236
}
237
8 months ago
238
# API
8 months ago
17
239
8 months ago
240
## Async
241
242
The pattern for using async is:
243
244
void *myasyncmaintask(void *param){
245
// do your main async task things here, like starting more tasks
246
}
247
248
Async_StartSystem();
249
void *res = NULL;
250
bool canceled = Async_Run(myasyncmaintask, NULL, &res);
251
Async_StopSystem();
252
253
To create and wait for an async task:
254
255
Async_Task task1;
256
Async_Task_ctor(&task1, asynctask1, &task1param);
257
void *res = NULL;
258
bool canceled = Async_Task_Await(&task1, void **res)
259
Async_Task_dtor(&task1);
260
261
or, if you prefer new & delete:
262
263
Async_Task *task1 = Async_Task_New(asynctask1, &task1param);
264
void *res = NULL;
265
bool canceled = Async_Task_Await(task1, void **res)
266
Async_Task_Delete(task1);
267
268
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:
269
270
Async_Future future;
271
Async_Future_ctor(&future);
272
273
// keep &future to hand for when the background thing completes
274
bool canceled = Async_Future_Await(&future, NULL);
275
276
Async_Future_dtor(&future);
277
278
`Async_Future_New()` and `Async_Future_Delete()` are also available if you prefer that style.
279
280
Inside the callback when the background thing is complete:
281
282
// result is a void *
283
Async_Future_SetResult(future, result, false);
284
285
or, if something went wrong:
286
287
// exception is a void *
288
Async_Future_SetResult(future, exception, true);
289
290
Back in the task, you can respond to the future:
291
292
... Async_Future_Await has returned
293
if (canceled){
294
// exit quickly - you've been canceled
295
// you could, for example, use the future's result as an exception, or error code here
296
}
297
// carry on - the future's result may be an actual result, that's up to you
298
299
8 months ago
300
##### void Async_Future_ctor(Async_Future *fut)
8 months ago
301
8 months ago
302
fut
8 months ago
303
: The `Async_Future` being constructed
8 months ago
304
8 months ago
305
Initialise a future. When you no longer need it, use `Async_Future_dtor()`.
306
8 months ago
307
##### Async_Future *Async_Future_New()
8 months ago
308
8 months ago
309
(returns)
8 months ago
310
: The new future
8 months ago
311
8 months ago
312
Allocates and initialises a future, When you no longer need it, use `Async_Future_Delete()`.
8 months ago
313
8 months ago
314
##### void Async_Future_dtor(Async_Future *fut)
8 months ago
315
316
fut
317
: The `Async_Future` being destructed
318
319
Destruct a future previously constructed with `Async_Future_ctor()`.
320
8 months ago
321
##### void Async_Future_Delete(Async_Future *fut)
8 months ago
322
8 months ago
323
fut
324
: The `Async_Future` to be destructed and freed
325
8 months ago
326
Delete (finalise and free) a future previously new'ed with `Async_Future_New()`
327
8 months ago
328
##### void Async_Future_SetResult(Async_Future *fut, bool canceled, void *value)
8 months ago
329
8 months ago
330
fut
331
: The `Async_Future` whose result is being set
8 months ago
332
8 months ago
333
canceled
334
: The future's `canceled` setting
8 months ago
335
8 months ago
336
value
337
: The future's `value` of the result
8 months ago
338
8 months ago
339
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 `Async_Future` has a result, its watchers are called back.
8 months ago
340
8 months ago
341
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
342
8 months ago
343
##### bool Async_Future_GetResult(Async_Future *fut, void **res)
8 months ago
344
8 months ago
345
(returns)
346
: The `canceled` value of the `Async_Future`.
8 months ago
347
8 months ago
348
res
349
: Where to store the value of the `Async_Future`. This may be `NULL`.
8 months ago
350
8 months ago
351
Get the result of a future.
8 months ago
352
8 months ago
353
##### typedef void (*Future_Watcher)(void *me, Async_Future *fut)
8 months ago
354
8 months ago
355
A `Future_Watcher` is a callback called when a future has a result. The `me` parameter is the one passed to `Async_Future_AddWatcher()`. `fut` is the future which has just got its result.
8 months ago
356
8 months ago
357
##### void Async_Future_AddWatcher(Async_Future *fut, Future_Watcher watcher, void *me)
8 months ago
358
8 months ago
359
fut
360
: the `Async_Future` to add a watcher to
361
362
watcher
363
: the callback to call when the `Async_Future` has a result.
364
365
me
366
: the `me` value to pass to `watcher` when it is called back.
367
368
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.
369
8 months ago
370
##### void Async_Future_RemoveWatcher(Async_Future *fut, Future_Watcher watcher, void *me)
8 months ago
371
372
fut
373
: the `Async_Future` to remove a watcher from
374
375
watcher
376
: the callback of the watcher to remove.
377
378
me
379
: the `me` value of the watcher to remove.
380
381
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.
382
8 months ago
383
##### bool Async_Future_Await(Async_Future *fut, void **res)
8 months ago
384
385
(returns)
386
: whether the `Async_Future` was canceled.
387
388
fut
389
: The `Async_Future` to wait for.
390
391
res
392
: Where to store the `value` of the future when it is has a result. May be `NULL`.
393
394
The current `Async_Task` is paused until the `Async_Future` has a result. Other `Async_Task`s are run while this one is waiting.
395
8 months ago
396
##### typedef bool (*Async_Entry)(void *param, void **res)
8 months ago
397
8 months ago
398
The entry function to an `Async_Task`.
399
8 months ago
400
##### void Async_Task_ctor(Async_Task *tsk, Async_Entry entry, void *param)
8 months ago
401
402
tsk
403
: The task to construct.
404
405
entry
406
: The entry function for the task.
407
408
param
409
: The value for `param` to pass to `entry`.
410
411
Initialises an `Async_Task`. When you have finished with an `Async_Task` you must finalise it using `Async_Task_dtor()`
412
413
Async_Task tsk;
414
Async_Task_ctor(&tsk, mytask, myparam);
415
// tsk will run if you wait for a task or future
416
Async_Task_Await(&tsk, NULL);
417
Async_Task_dtor(&tsk);
418
8 months ago
419
##### Async_Task *Async_Task_New(Async_Entry entry, void *param)
8 months ago
420
421
(returns)
422
: The new `Async_Task`.
423
424
entry
425
: The entry function for the task.
426
427
param
428
: The value for `param` to pass to `entry`.
429
430
This allocates and initialises a new `Async_Task`. When you have finished with your task, you must `Async_Delete()` it.
431
432
Async_Task *tsk = Async_New(mytask, myparam);
433
// tsk will run if you wait for a task or future
434
Async_Task_Await(tsk, NULL);
435
Async_Task_Delete(tsk);
436
8 months ago
437
##### void Async_Task_dtor(Async_Task *tsk)
8 months ago
438
439
tsk
440
: The `Async_Task` to destruct.
441
442
This finalises an `Async_Task` you ealier initalised with `Async_Task_ctor()`. It is an error to attempt to destruct a task which is running.
443
444
Async_Task tsk;
445
Async_Task_ctor(&tsk, mytask, myparam);
446
// use tsk
447
Async_Task_dtor(&tsk);
448
8 months ago
449
##### void Async_Task_Delete(Async_Task *tsk)
8 months ago
450
451
tsk
452
: The `Async_Task` to delete.
453
454
This finalises and frees an `Async_Task` you ealier new'ed with `Async_Task_New()`. It is an error to attempt to delete a task which is running.
455
456
Async_Task *tsk = Async_Task_New(mytask, myparam);
457
// use tsk
458
Async_Task_Delete(tsk);
459
8 months ago
460
##### static inline bool Async_Task_Await(Async_Task *tsk, void **res)
8 months ago
461
462
(returns)
463
: Whether the task was canceled.
464
465
tsk
466
: The `Async_Task` to wait for.
467
468
res
469
: Where to store the `Async_Task`'s value when it finishes. This may be NULL.
470
471
The current `Async_Task` waits for `tsk` to finish, and returns the result.
472
8 months ago
473
##### void Async_Task_Cancel(Async_Task *tsk, void *cancel_value)
8 months ago
474
475
tsk
476
: The task to cancel.
477
478
cancel_value
479
: The value to set on any future this task waits on.
480
481
This marks a task as canceled. When that task waits on a future that future will be canceled too, using `cancel_value`.
482
8 months ago
483
##### static inline bool Async_Task_IsCanceled(Async_Task *tsk)
8 months ago
484
485
(returns)
486
: Whether the task is canceled.
487
488
tsk
489
: The task to get its canceled setting from.
490
8 months ago
491
##### static inline Async_Future *Async_Task_GetAwaitedFuture(Async_Task *tsk)
8 months ago
492
8 months ago
493
(returns)
494
: The future the task is waiting on. May be NULL.
495
496
tsk
497
: Teh task to read the future it is waiting on.
498
499
Return the future a task is waiting on.
500
8 months ago
501
##### bool Async_Sleep(float delay, void **value)
8 months ago
502
503
(returns)
504
: Whether the task was canceled.
505
506
delay
507
: How many seconds to delay for.
508
509
value
510
: Where to store the cancellation value. This may be NULL.
511
512
Sleep for `delay` seconds. `*value` will be set to NULL if the sleep is successful, and the `cancel_value` if the task is canceled.
513
8 months ago
514
##### void Async_StartSystem()
8 months ago
515
516
Start the async system on this thread. If you want to use async features, use this instead of `Coroutine_StartSystem()`. When you have finished with the async system, use `Async_StopSystem()`.
517
518
Async_StartSystem();
519
// used async here
520
Async_StopSystem();
521
8 months ago
522
##### bool Async_Run(Async_Entry start, void *value, void **res)
8 months ago
523
524
(returns)
525
: Whether `start` was canceled.
526
527
start
528
: The function to use as the main task.
529
530
value
531
: The value to pass to `start`.
532
533
res
534
: Where to store the result of `start`.
535
536
Runs `start` as an `Async_Task`. When `start` returns all other tasks must have been destructed, using `Async_Task_dtor()` or `Async_Task_Delete()`.
537
8 months ago
538
##### void Async_StopSystem()
8 months ago
539
8 months ago
540
Stops the async system. When you have finished using the async system, you must stop it.
8 months ago
541
8 months ago
542
Async_Start();
543
// use the async system
544
Async_Stop();
545
8 months ago
546
## Generator
8 months ago
17
547
8 months ago
548
The pattern for a `Generator` is:
549
8 months ago
550
#### A loop which uses the `Generator
8 months ago
551
8 months ago
17
552
Generator gen;
8 months ago
553
Generator_ctor(&gen, mygen, &param);
8 months ago
17
554
8 months ago
555
void *value;
556
while(Generator_Next(&gen, &value)){
557
// use value here
8 months ago
17
558
}
8 months ago
559
// value is now the return value from the Generator
560
8 months ago
17
561
Generator_dtor(&gen);
562
8 months ago
563
Or:
8 months ago
17
564
8 months ago
565
Generator *gen = Generator_New(mygen, &param);
8 months ago
17
566
8 months ago
567
void *value;
568
while(Generator_Next(gen, &value)){
569
// use value here
570
}
8 months ago
17
571
8 months ago
572
Generator_Delete(gen);
8 months ago
17
573
8 months ago
574
`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.
8 months ago
17
575
8 months ago
576
#### A generator function
8 months ago
17
577
8 months ago
578
void *mygen(void *param){
579
bool domore = true;
580
// The parameter is a pointer to a string of chars
581
for (char *str = param; *str; ++str) {
582
// The value yielded is a pointer to a character in the string
583
domore = Generator_Yield(str);
584
if (!domore){
585
break;
586
}
587
}
588
589
return (void *)domore;
8 months ago
17
590
}
591
8 months ago
592
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
593
8 months ago
594
##### void Generator_ctor(Generator *gen, void *(*start)(void *), void *param)
8 months ago
17
595
8 months ago
596
gen
597
: The `Generator` to construct.
8 months ago
17
598
8 months ago
599
start
600
: The function which is the start/entry-point of the `Generator`.
601
602
param
603
: The value to pass to `start`.
604
605
Initialise a `Generator`. When you no longer need the `Generator`, use `Generator_dtor()` to destruct it.
606
8 months ago
607
Generator gen;
608
Generator_ctor(&gen, mystart, &params);
8 months ago
17
609
8 months ago
610
// Generator is used
8 months ago
17
611
8 months ago
612
// ... later:
613
Generator_dtor(&gen);
614
8 months ago
615
##### Generator *Generator_New(void *(*start)(void *), void *param)
8 months ago
616
8 months ago
617
start
618
: The function which is the start/entry-point of the `Generator`.
8 months ago
619
8 months ago
620
param
621
: The value to pass to `start`.
622
623
`new` a `Generator` - malloc, and initialise it. When you no longer need the `Generator` use `Generator_dtor` to finalise it.
624
8 months ago
625
Generator *gen = Generator_New(mystart, &params);
626
627
// Generator is used
628
629
// ... later:
630
Generator_Delete(gen);
631
8 months ago
632
##### void Generator_dtor(Generator *gen)
8 months ago
633
8 months ago
634
gen
635
: The `Generator` to destruct.
636
8 months ago
637
Finalise a `Generator`. Once a `Generator` is no longer needed, it must be finalised:
638
639
// earlier...
640
Generator gen;
641
Generator_ctor(&gen, mystart, &params);
642
643
// Generator is used
644
645
// the Generator is no longer needed
646
Generator_dtor(&gen);
647
648
8 months ago
649
##### void Generator_Delete(Generator *gen)
8 months ago
650
8 months ago
651
gen
652
: The `Generator` to delete.
653
8 months ago
654
Finalise then `free()` a `Generator`. Once a `new`ed `Generator` is no longer needed, it must be deleted:
655
656
// earlier...
657
Generator *gen = Generator_New(mystart, &params);
658
659
// Generator is used
660
661
// the Generator is no longer needed
662
Generator_Delete(gen);
663
664
8 months ago
665
##### bool Generator_Next(Generator *gen, void **value)
8 months ago
666
8 months ago
667
(returns)
668
: Whether there is a next value. `true` - there is a next value; `false` - the `Generator` has finished
669
670
gen
671
: The `Generator` to get the next value from.
672
673
value
674
: Where to store the next value.
675
8 months ago
676
Get the next value yielded by the `Generator`.
677
678
void *value;
679
while(Generator_Next(gen, &value)){
680
// use value here
8 months ago
17
681
}
8 months ago
682
8 months ago
683
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
684
8 months ago
685
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`.
686
8 months ago
687
##### bool Generator_Yield(void *value)
8 months ago
688
8 months ago
689
(returns)
690
: Whether the `Generator` should do more.
8 months ago
691
8 months ago
692
value
693
: The `Generator`'s next value.
694
695
Yield a value from a `Generator`.
696
8 months ago
697
bool domore = Generator_Yield(value);
698
8 months ago
699
`value` is then provided by `Generator_Next()` as the next value from the generator.
8 months ago
700
8 months ago
701
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.
702
8 months ago
703
## Coroutine
704
8 months ago
705
##### void Coroutine_StartSystem()
8 months ago
706
8 months ago
707
Start the coroutine system on this thread. When you've finished with `Coroutine` must call `Coroutine_Stop()`.
8 months ago
708
8 months ago
709
Coroutine_Start();
710
// prepare
711
void *result = Coroutine_Run(...);
712
// use result
713
Coroutine_Stop();
8 months ago
714
8 months ago
715
`Coroutine` can be started & stopped many times. While `Coroutine` is started, `Coroutine_Run()` can be called any number of times.
716
717
The total stack allowed for all coroutines running on any thread is the size of the call stack on that thread.
718
8 months ago
719
##### void Coroutine_StopSystem()
8 months ago
720
8 months ago
721
Stop the coroutine system on this thread.
722
8 months ago
723
##### Coroutine_Start
8 months ago
724
725
void *(*)(void *param)
726
8 months ago
727
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
728
8 months ago
729
##### Coroutine *Coroutine_New(Coroutine_Start start)
8 months ago
730
731
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.
732
8 months ago
733
##### void Coroutine_Run_Coroutine(Coroutine *cor, void *value)
8 months ago
734
735
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.
736
8 months ago
737
##### void *Coroutine_Run(Coroutine_Start start, void *value)
8 months ago
738
739
Convenience wrapper for `Coroutine_Run_Coroutine` which creates the `Coroutine` and retrieves its result.
740
8 months ago
741
##### void Coroutine_Delete(Coroutine *cor)
8 months ago
742
8 months ago
743
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
744
8 months ago
745
##### void Coroutine_Continue(Coroutine *cor, void *value, bool early)
8 months ago
746
747
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.
748
8 months ago
749
##### void *Coroutine_Yield(void *value, Coroutine_YieldCallback on_yield, void *this)
8 months ago
750
751
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()`.
752
8 months ago
753
##### void *Coroutine_GetValue(Coroutine *cor)
8 months ago
754
755
Return the `Coroutine`'s value - the value last yielded, or returned by its `start` routine.
756
8 months ago
757
##### Coroutine *Coroutine_GetActive()
8 months ago
758
759
Return whihc coroutine is currently running, ie the caller's `Coroutine`.
760
8 months ago
761
##### bool Coroutine_IsRunning(Coroutine *cor)
8 months ago
762
763
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
764
8 months ago
765
##### int Coroutine_GetStackHeadroom()
8 months ago
766
8 months ago
767
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
768
8 months ago
769
##### bool Coroutine_HasCoroutinesInFreePool()
8 months ago
770
771
Return whether the coroutine system has any coroutine stacks available in its coroutine stack free pool.
772
8 months ago
773
##### void *Coroutine_GetCStackTop()
8 months ago
774
775
Return an address which is near to the top of used C stack.
776
8 months ago
777
##### void *Coroutine_Chain(Coroutine_Start start, void *value)
8 months ago
778
779
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()`.
780