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