| 1 | # ccoroutines |
| 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, ¶m, &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 | } |
| 123 | |