| # 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, ¶m, &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); |
| ... |
| } |
|