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