0 branches 0 tags
15 16
17
18 19
Documentation started
on 6:47 PM Sep 10 2025
README.md
16
17
1
2
3
# ccoroutines
Coroutines in C, using standard libraries
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
# ccoroutines
Coroutines in C, using standard libraries.
## What's Here
### ccoroutines
The coroutine engine.
### generator
Provides generators - coroutines which yield values. These are especially useful for nested searches which provide values to a for loop (eg searching for files in a directory tree).
### async
Provides asynchronous tasks. Each task volunteers to be switched away-from by waiting on a `Async_Future`.
Note that `generator` and `async` can be used together.
## Prerequisites
These 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.
### Style
The style is influenced by C++. For example, where possible, a `Something *Something_New(a, b, c)` and `Something_Delete(Something *)`, where a `Something` is `malloc`ed, will have corresponding `Somthing_ctor(Somthing *, a, b, c)` and `Something_dtor(Something *)` to initialise and finalise a `Something` on the stack, or within another object. Using `.._ctor()` and `.._dtor()` will be faster as they avoid the `malloc()` and `free()`.
Something *oneofthem = Something_New();
// use oneofthem
Something_Delete(oneofthem);
Can be also be done like this, and this will run faster:
Something oneofthem;
Something_ctor(&oneofthem);
// use oneofthem
Something_dtor(&oneofthem);
## Usage
When you are using coroutines or generators:
void *myfunc(void *){
// your function here
}
Coroutine_StartSystem();
Coroutine_Run(myfunc, (void *)myparam);
Coroutine_StopSystem();
If you also use async, then:
bool myfunc(void *myparam, void **res){
// your async function here
}
Async_StartSystem();
void *res = NULL;
bool canceled = Async_Run(myfunc, myparam, &res);
Async_StopSystem();
While the system is started, you can make many calls to `Coroutine_Run()` or `Async_Run()`. A running system is thread local - each thread you want to use coroutines on will need to be `Coroutine_StartSystem()`ed or `Async_StartSystem()`ed.
### Generators
void *yield_files(void *param){
bool domore = true;
// loop/call functions to find more values to yield, and when you have one:
domore = Generator_Yield(res);
// .. 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;
}
Generator gen;
Generator_ctor(&gen, yield_files, "..");
int count = 0;
void *res;
while(Generator_Next(&gen, &res)){
// use res - a value yielded by your generator
printf("%d) %s\n", count, (char *)res);
free(res);
// exit your loop early if you want to
if (++count>16000) break;
}
Generator_dtor(&gen);
### Async
To run an Async program:
Async_StartSystem();
void *res = NULL;
bool canceled = Async_Run(asyncmain, &param, &res);
Async_StopSystem();
Async runs tasks, switching between them when the current task waits on an `Async_Future`. The async main is also a task. The entry function for any task looks like this:
bool asyncmain(void *param, void **res){
// do your thing here
return canceled;
}
Tasks can complete, or be canceled. Return whether your task was canceled or not.
Within your async task, create `Async_Task`s and `Async_Future_Await()` them when you want to wait for their result. Note that an `Async_Task` has a member, `base`, which is an `Async_Future`, which is what you should wait on:
bool asyncmain(void *param, void **res){
...
Async_Task task1;
Async_Task_ctor(&task1, anasynctask, &task1param);
bool canceled = Async_Future_Await(&task1.base);
// use the result stored in the future
Async_Task_dtor(&task1);
...
}