2 contributors
178 lines6.6 KB
Newer
Older
-
+
commited
{line.log.rev}
on
10 months ago
1
1
# ccoroutines
9 months ago
2
Coroutines in C, using standard libraries.
3
4
## What's Here
5
6
### ccoroutines
7
The coroutine engine.
8
9
### generator
10
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).
11
12
### async
13
Provides asynchronous tasks. Each task volunteers to be switched away-from by waiting on a `Async_Future`.
14
15
Note that `generator` and `async` can be used together.
16
17
## Prerequisites
18
19
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.
20
21
### Style
22
23
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()`.
24
25
Something *oneofthem = Something_New();
26
// use oneofthem
27
Something_Delete(oneofthem);
28
29
Can be also be done like this, and this will run faster:
30
31
Something oneofthem;
32
Something_ctor(&oneofthem);
33
// use oneofthem
34
Something_dtor(&oneofthem);
35
36
## Usage
37
38
When you are using coroutines or generators:
39
40
void *myfunc(void *){
41
// your function here
42
}
43
44
Coroutine_StartSystem();
45
Coroutine_Run(myfunc, (void *)myparam);
46
Coroutine_StopSystem();
47
48
If you also use async, then:
49
50
bool myfunc(void *myparam, void **res){
51
// your async function here
52
}
53
54
Async_StartSystem();
55
void *res = NULL;
56
bool canceled = Async_Run(myfunc, myparam, &res);
57
Async_StopSystem();
58
59
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.
60
61
### Generators
62
63
void *yield_files(void *param){
64
bool domore = true;
65
66
// loop/call functions to find more values to yield, and when you have one:
67
domore = Generator_Yield(res);
68
// .. if domore is false, exit your generator - it is being destructed
69
70
// not actually used by generators, but this is a useful convention for bubbling
71
// the flag out to calling functions.
72
return (void *)domore;
73
}
74
75
Generator gen;
76
Generator_ctor(&gen, yield_files, "..");
77
int count = 0;
78
void *res;
79
while(Generator_Next(&gen, &res)){
80
// use res - a value yielded by your generator
81
printf("%d) %s\n", count, (char *)res);
82
free(res);
83
84
// exit your loop early if you want to
85
if (++count>16000) break;
86
}
87
Generator_dtor(&gen);
88
89
### Async
90
91
To run an Async program:
92
93
Async_StartSystem();
94
void *res = NULL;
95
bool canceled = Async_Run(asyncmain, &param, &res);
96
Async_StopSystem();
97
98
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:
99
100
bool asyncmain(void *param, void **res){
101
102
// do your thing here
103
104
return canceled;
105
}
106
107
Tasks can complete, or be canceled. Return whether your task was canceled or not.
108
109
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:
110
111
bool asyncmain(void *param, void **res){
112
...
113
Async_Task task1;
114
Async_Task_ctor(&task1, anasynctask, &task1param);
115
116
bool canceled = Async_Future_Await(&task1.base);
117
118
// use the result stored in the future
119
120
Async_Task_dtor(&task1);
121
...
122
}
9 months ago
123
124
# API
125
126
## Coroutine
127
128
### void Coroutine_StartSystem();
129
130
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.
131
132
### void Coroutine_StopSystem();
133
134
Stop the coroutine system on this thread.
135
136
### Coroutine_Start
137
138
void *(*)(void *param)
139
140
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.
141
142
### Coroutine *Coroutine_New(Coroutine_Start start);
143
144
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.
145
146
### void Coroutine_Run_Coroutine(Coroutine *cor, void *value);
147
148
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.
149
150
### void *Coroutine_Run(Coroutine_Start start, void *value);
151
152
Convenience wrapper for `Coroutine_Run_Coroutine` which creates the `Coroutine` and retrieves its result.
153
154
### void Coroutine_Delete(Coroutine *cor);
155
156
Use `Coroutine_Delete()` to delete a coroutine when it is no longer needed.
157
158
### void Coroutine_Continue(Coroutine *cor, void *value, bool early);
159
160
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.
161
162
### void *Coroutine_Yield(void *value, Coroutine_YieldCallback on_yield, void *this);
163
164
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()`.
165
166
### void *Coroutine_GetValue(Coroutine *cor);
167
168
Return the `Coroutine`'s value - the value last yielded, or returned by its `start` routine.
169
170
### Coroutine *Coroutine_GetActive();
171
172
Return whihc coroutine is currently running, ie the caller's `Coroutine`.
173
174
### bool Coroutine_IsRunning(Coroutine *cor);
175
176
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.
177