0 branches 0 tags
24 25
26
27 28
Update readme
on 2:26 PM Sep 12 2025
trunk/README.md
25
26
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
# 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);
...
}
# API
## Coroutine
### void Coroutine_StartSystem();
Start the coroutine system on this thread. When you've finished with `Coroutine` call `Coroutine_Stop()`. `Coroutine` can be started & stopped many times on one thread. The total stack allowed for all coroutines running on a thread is the size of the call stack on that thread.
### void Coroutine_StopSystem();
Stop the coroutine system on this thread.
### Coroutine_Start
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.
### Coroutine *Coroutine_New(Coroutine_Start start);
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.
### void Coroutine_Run_Coroutine(Coroutine *cor, void *value);
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.
### void *Coroutine_Run(Coroutine_Start start, void *value);
Convenience wrapper for `Coroutine_Run_Coroutine` which creates the `Coroutine` and retrieves its result.
### void Coroutine_Delete(Coroutine *cor);
Use `Coroutine_Delete()` to delete a coroutine when it is no longer needed.
### void Coroutine_Continue(Coroutine *cor, void *value, bool early);
Continue the given `Coroutine`. `value` is passed to the coroutine, as `param` to the `start` function, or as the return value from `Coroutine_Yield`. `early` determines whether the continued coroutine is added to the head of tail of the list of runable coroutines.
### void *Coroutine_Yield(void *value, Coroutine_YieldCallback on_yield, void *this);
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);
Return the `Coroutine`'s value - the value last yielded, or returned by its `start` routine.
### Coroutine *Coroutine_GetActive();
Return whihc coroutine is currently running, ie the caller's `Coroutine`.
### bool Coroutine_IsRunning(Coroutine *cor);
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.
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
Stackful coroutines in C.
* `Async` coroutines which can pause, waiting for a future.
* `Generator` coroutines used as generators for loops.
* `Coroutine` the coroutine engine used by `Async` and `Generator`.
Your code doesn't need to do anything special to be a coroutine, and only standard, or commonly available libraries are needed.
## Prerequisites
The goal was to make a system which can be used 'out of the box'. These libraries rely on as much as possible on C's cross-platform comfort zone. C's standard libraries are used as far as possible, but, as `threads.h` is not usually supported, `pthread.h` has been used instead.
You will need to build & link the code as part of your system - `coroutine/async.c`, `coroutine/generator.c` and `coroutine/coroutine.c` - ensure the headers, `include/*`, are available on your include path.
## Quick Start
### Async
To run an Async program:
#include "async.h"
main(){
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`. `asyncmain()` is run as a task. The start function for any task looks like this:
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 async task, create `Async_Task`s and `Async_Task_Await()` them when you want to wait for their result:
Async_Task task1;
Async_Task_ctor(&task1, adifferenttask, &task1param);
void *result;
bool canceled = Async_Task_Await(&task1, &result);
Async_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`:
Async_Future future;
Async_Future_ctor(&future);
// pass the future to the background-thing-which-might-take-a-while
bool canceled = Async_Future_Await(&future);
// get the result, if you want it, from the future
Async_Future_dtor(&future);
When the background-thing-which-might-take-a-while has a result:
Async_Future_SetResult(future, false, result);
### Generators
The coroutine system needs to be started, either through `Async_StartSystem()`, or directly with `Coroutine_StartSystem()` if you don't want to do async things.
You will need a generator function:
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:
Generator gen;
Generator_ctor(&gen, 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`.
The Coroutines system must be started:
Coroutine_StartSystem();
// use coroutines here
Coroutine_StopSystem();
Your coroutine will need to have a start function:
void *start(void *param){
...
}
When there is no coroutine running, start your 'main' coroutine:
void *result = Coroutine_Run(comain, param);
Create other coroutines like this:
Coroutine *cor = Coroutine_New(start);
When you want a Coroutine to run, or to return from a yield:
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:
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 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.
As 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.
## 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);
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:
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.
# API
## Generator
The pattern for a `Generator` is:
#### A loop which uses the `Generator`
Generator gen;
Generator_ctor(&gen, 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:
Generator *gen = Generator_New(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.
#### A generator function
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, void *(*start)(void *), void *param)
Initialise a `Generator`
Generator gen;
Generator_ctor(&gen, mystart, &params);
// Generator is used
// ... later:
Generator_dtor(&gen);
### Generator *Generator_New(void *(*)(void *), void *)
`new` a `Generator` - malloc it and initialise it.
Generator *gen = Generator_New(mystart, &params);
// Generator is used
// ... later:
Generator_Delete(gen);
### void Generator_dtor(Generator *gen)
Finalise a `Generator`. Once a `Generator` is no longer needed, it must be finalised:
// earlier...
Generator gen;
Generator_ctor(&gen, mystart, &params);
// Generator is used
// the Generator is no longer needed
Generator_dtor(&gen);
### void Generator_Delete(Generator *)
Finalise then `free()` a `Generator`. Once a `new`ed `Generator` is no longer needed, it must be deleted:
// earlier...
Generator *gen = Generator_New(mystart, &params);
// Generator is used
// the Generator is no longer needed
Generator_Delete(gen);
### bool Generator_Next(Generator *, void **value)
Get the next value yielded by the `Generator`.
void *value;
while(Generator_Next(gen, &value)){
// use value here
}
When `true` is returned, `value` is the value yielded by the `Generator`. When `false` is returned, `value` is the value returned when the `Generator` returned - the `Generator` has finished.
### bool Generator_Yield(void *)
Yield a value from a `Generator` function.
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()` indicates whether more values should be provided by your generator function. When `true` provide more values if possible. When `false` close files, free memory, free up any other resources and `return`. `false` is returned when the `Generator` is being finalised.
## Coroutine
### void Coroutine_StartSystem();
Start the coroutine system on this thread. When you've finished with `Coroutine` call `Coroutine_Stop()`. `Coroutine` can be started & stopped many times on one thread. The total stack allowed for all coroutines running on a thread is the size of the call stack on that thread.
### void Coroutine_StopSystem();
Stop the coroutine system on this thread.
### Coroutine_Start
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.
### Coroutine *Coroutine_New(Coroutine_Start start)
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.
### void Coroutine_Run_Coroutine(Coroutine *cor, void *value)
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.
### void *Coroutine_Run(Coroutine_Start start, void *value)
Convenience wrapper for `Coroutine_Run_Coroutine` which creates the `Coroutine` and retrieves its result.
### void Coroutine_Delete(Coroutine *cor)
Use `Coroutine_Delete()` to delete a coroutine when it is no longer needed.
### void Coroutine_Continue(Coroutine *cor, void *value, bool early)
Continue the given `Coroutine`. `value` is passed to the coroutine, as `param` to the `start` function, or as the return value from `Coroutine_Yield`. `early` determines whether the continued coroutine is added to the head of tail of the list of runable coroutines.
### void *Coroutine_Yield(void *value, Coroutine_YieldCallback on_yield, void *this)
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)
Return the `Coroutine`'s value - the value last yielded, or returned by its `start` routine.
### Coroutine *Coroutine_GetActive()
Return whihc coroutine is currently running, ie the caller's `Coroutine`.
### bool Coroutine_IsRunning(Coroutine *cor)
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.