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