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