diff options
| -rw-r--r-- | docs/ccommon_api.md | 28 | ||||
| -rw-r--r-- | include/stc/algo/coroutine.h | 49 | ||||
| -rw-r--r-- | misc/examples/cointerleave.c | 60 | ||||
| -rw-r--r-- | misc/examples/coread.c | 14 | ||||
| -rw-r--r-- | misc/examples/coroutines.c | 20 | ||||
| -rw-r--r-- | misc/examples/scheduler.c | 76 | ||||
| -rw-r--r-- | misc/examples/triples.c | 8 |
7 files changed, 212 insertions, 43 deletions
diff --git a/docs/ccommon_api.md b/docs/ccommon_api.md index 549eff4e..27df13ce 100644 --- a/docs/ccommon_api.md +++ b/docs/ccommon_api.md @@ -302,13 +302,13 @@ struct triples { int cco_state; // required member }; -bool triples_next(struct triples* i) { // coroutine +bool triples(struct triples* i) { // coroutine cco_begin(i); for (i->c = 5; i->n; ++i->c) { for (i->a = 1; i->a < i->c; ++i->a) { for (i->b = i->a + 1; i->b < i->c; ++i->b) { if ((int64_t)i->a*i->a + (int64_t)i->b*i->b == (int64_t)i->c*i->c) { - cco_yield(true); + cco_yield(false); if (--i->n == 0) cco_return; } } @@ -316,7 +316,7 @@ bool triples_next(struct triples* i) { // coroutine } cco_final: // required label puts("done"); - cco_end(false); + cco_end(true); } int gcd(int a, int b) { // greatest common denominator @@ -333,7 +333,7 @@ int main() struct triples t = {.n=INT32_MAX}; int n = 0; - while (triples_next(&t)) { + while (!triples(&t)) { // Skip triples with GCD(a,b) > 1 if (gcd(t.a, t.b) > 1) continue; @@ -353,20 +353,24 @@ To resume the coroutine from where it was suspended with *cco_yield()*, simply c | | Function / operator | Description | |:----------|:-------------------------------------|:----------------------------------------| | | `cco_final:` | Obligatory label in coroutine | -| | `cco_return;` | Early return from the coroutine | +| | `cco_return` | Early return from the coroutine (no arg) | | `bool` | `cco_alive(ctx)` | Is coroutine in initial or suspended state? | | `bool` | `cco_done(ctx)` | Is coroutine not alive? | | `bool` | `cco_suspended(ctx)` | Is coroutine in suspended state? | -| `void` | `cco_begin(ctx)` | Begin coroutine block | -| `rettype` | `cco_end(retval)` | End coroutine block and return retval | -| `void` | `cco_end()` | End coroutine block (return void) | -| `rettype` | `cco_yield(retval)` | Suspend execution and return retval | -| `void` | `cco_yield()` | Suspend execution (return void) | -| `rettype` | `cco_await_while(cond, retval)` | If cond, suspend execution and return retval | -| `bool` | `cco_await(cond)` | If not cond, suspend execution and return true | +| | `cco_begin(ctx)` | Begin coroutine block | +| | `cco_end(retval)` | End coroutine block and return retval | +| | `cco_end()` | End coroutine block (return void) | +| | `cco_yield(retval)` | Suspend execution and return retval | +| | `cco_yield()` | Suspend execution (return void) | +| | `cco_await(promise)` | Suspend and return false until promise is true | +| | `cco_await_while(cond, retval)` | Suspend and return retval while cond is true | | | From caller side: | | | `void` | `cco_stop(ctx)` | Next call of coroutine returns `cco_end()` | | `void` | `cco_reset(ctx)` | Reset state to initial (for reuse) | +| | Semaphores: | | +| | `cco_semaphore` | Semaphore type | +| | `cco_await_sem(sem)` | Await for the semaphore count > 0 | +| | `cco_signal_sem(sem)` | Signal the semaphore by increasing count| --- ## RAII scope macros diff --git a/include/stc/algo/coroutine.h b/include/stc/algo/coroutine.h index 6c1d7d28..4b7bee5f 100644 --- a/include/stc/algo/coroutine.h +++ b/include/stc/algo/coroutine.h @@ -26,38 +26,38 @@ #include <stdio.h> #include <stc/algo/coroutine.h> -struct iterate { +struct coroutine { int max_x, max_y; int x, y; int cco_state; // required member }; -bool iterate(struct iterate* I) { +bool coroutine(struct coroutine* I) { cco_begin(I); for (I->x = 0; I->x < I->max_x; I->x++) for (I->y = 0; I->y < I->max_y; I->y++) - cco_yield(true); + cco_yield(false); cco_final: - puts("final"); - cco_end(false); + puts("final"); + cco_end(true); } int main(void) { - struct iterate it = {.max_x=3, .max_y=3}; + struct coroutine it = {.max_x=3, .max_y=3}; int n = 0; - while (iterate(&it)) + while (!coroutine(&it)) { printf("%d %d\n", it.x, it.y); // example of early stop: - if (++n == 20) cco_stop(&it); // signal to stop at next + if (++n == 7) cco_stop(&it); // signal to stop at next } return 0; } */ #include <stc/ccommon.h> -enum { +enum cco_states { cco_state_final = -1, cco_state_done = -2, }; @@ -89,7 +89,7 @@ enum { case __LINE__: if (cond) return retval; \ } while (0) -#define cco_await(cond) cco_await_while(!(cond), true) +#define cco_await(promise) cco_await_while(!(promise), false) #define cco_final \ case cco_state_final: \ @@ -110,4 +110,33 @@ enum { if (*_state == cco_state_done) *_state = 0; \ } while (0) + +typedef struct { + int count; +} cco_semaphore; + +/** + * Wait for a semaphore + * + * This macro carries out the "wait" operation on the semaphore. The + * wait operation causes the "thread" to block while the counter is + * zero. When the counter reaches a value larger than zero, the + * protothread will continue. + */ +#define cco_await_sem(sem) \ + do { \ + cco_await((sem)->count > 0); \ + --(sem)->count; \ + } while (0) + +/** + * Signal a semaphore + * + * This macro carries out the "signal" operation on the semaphore. The + * signal operation increments the counter inside the semaphore, which + * eventually will cause waiting "threads" to continue executing. + */ +#define cco_signal_sem(sem) ++(sem)->count +#define cco_reset_sem(sem, value) ((sem)->count = value) + #endif diff --git a/misc/examples/cointerleave.c b/misc/examples/cointerleave.c new file mode 100644 index 00000000..64473d96 --- /dev/null +++ b/misc/examples/cointerleave.c @@ -0,0 +1,60 @@ +// https://www.youtube.com/watch?v=8sEe-4tig_A +#include <stc/calgo.h> +#include <stdio.h> +#define i_type IVec +#define i_val int +#include <stc/cvec.h> + +struct GenValue { + IVec *v; + IVec_iter it; + int cco_state; +}; + +static int next_value(struct GenValue* g) +{ + cco_begin(g); + for (g->it = IVec_begin(g->v); g->it.ref; IVec_next(&g->it)) + cco_yield(*g->it.ref); + cco_final: + cco_end(0); +} + +struct Generator { + struct GenValue x, y; + int cco_state; + int value; +}; + +bool interleaved(struct Generator* g) +{ + cco_begin(g); + while (cco_alive(&g->x) || cco_alive(&g->y)) + { + g->value = next_value(&g->x); + if (cco_alive(&g->x)) cco_yield(false); + + g->value = next_value(&g->y); + if (cco_alive(&g->y)) cco_yield(false); + } + cco_final: + cco_end(true); +} + +void Use(void) +{ + IVec a = c_make(IVec, {2, 4, 6, 8, 10, 11}); + IVec b = c_make(IVec, {3, 5, 7, 9}); + + struct Generator g = {{&a}, {&b}}; + + while (!interleaved(&g)) + printf("%d\n", g.value); + + c_drop(IVec, &a, &b); +} + +int main() +{ + Use(); +} diff --git a/misc/examples/coread.c b/misc/examples/coread.c index 0a7f4816..d5385a87 100644 --- a/misc/examples/coread.c +++ b/misc/examples/coread.c @@ -4,36 +4,36 @@ // Read file line by line using coroutines: -struct file_nextline { +struct file_read { const char* filename; int cco_state; FILE* fp; cstr line; }; -bool file_nextline(struct file_nextline* U) +bool file_read(struct file_read* U) { cco_begin(U) U->fp = fopen(U->filename, "r"); U->line = cstr_init(); while (cstr_getline(&U->line, U->fp)) - cco_yield(true); + cco_yield(false); cco_final: // this label is required. printf("finish\n"); cstr_drop(&U->line); fclose(U->fp); - cco_end(false); + cco_end(true); } int main(void) { - struct file_nextline it = {__FILE__}; + struct file_read g = {__FILE__}; int n = 0; - while (file_nextline(&it)) + while (!file_read(&g)) { - printf("%3d %s\n", ++n, cstr_str(&it.line)); + printf("%3d %s\n", ++n, cstr_str(&g.line)); //if (n == 10) cco_stop(&it); } } diff --git a/misc/examples/coroutines.c b/misc/examples/coroutines.c index af9fef81..3fddf913 100644 --- a/misc/examples/coroutines.c +++ b/misc/examples/coroutines.c @@ -23,7 +23,7 @@ bool prime(struct prime* U) { if (U->result == 2) { if (U->count-- == 0) cco_return; ++U->idx; - cco_yield(true); + cco_yield(false); } U->result += !(U->result & 1); for (U->pos = U->result; U->count > 0; U->pos += 2) { @@ -31,12 +31,12 @@ bool prime(struct prime* U) { --U->count; ++U->idx; U->result = U->pos; - cco_yield(true); + cco_yield(false); } } cco_final: printf("final prm\n"); - cco_end(false); + cco_end(true); } @@ -63,11 +63,11 @@ bool fibonacci(struct fibonacci* F) { F->result = F->b; F->b = sum; } - cco_yield(true); + cco_yield(false); } cco_final: printf("final fib\n"); - cco_end(false); + cco_end(true); } // Combine @@ -81,23 +81,23 @@ struct combined { bool combined(struct combined* C) { cco_begin(C); - cco_await(prime(&C->prm) == false); - cco_await(fibonacci(&C->fib) == false); + cco_await(prime(&C->prm)); + cco_await(fibonacci(&C->fib)); // Reuse the C->prm context and extend the count: C->prm.count = 8; C->prm.result += 2; cco_reset(&C->prm); - cco_await(prime(&C->prm) == false); + cco_await(prime(&C->prm)); cco_final: puts("final comb"); - cco_end(false); + cco_end(true); } int main(void) { struct combined comb = {.prm={.count=8}, .fib={14}}; - while (combined(&comb)) + while (!combined(&comb)) printf("Prime(%d)=%lld, Fib(%d)=%lld\n", comb.prm.idx, (long long)comb.prm.result, comb.fib.idx, (long long)comb.fib.result); diff --git a/misc/examples/scheduler.c b/misc/examples/scheduler.c new file mode 100644 index 00000000..1809f2ec --- /dev/null +++ b/misc/examples/scheduler.c @@ -0,0 +1,76 @@ +// https://www.youtube.com/watch?v=8sEe-4tig_A +#include <stdio.h> +#include <stc/calgo.h> + +struct Scheduler; +struct Task { + bool (*resume)(struct Task*); + struct Scheduler* sched; + int cco_state; +}; + +#define i_type Scheduler +#define i_val struct Task +#define i_no_cmp +#include <stc/clist.h> + +static bool schedule(Scheduler* sched) +{ + struct Task task = *Scheduler_front(sched); + Scheduler_pop_front(sched); + + if (cco_alive(&task)) + task.resume(&task); + + return !Scheduler_empty(sched); +} + +static bool resume_task(const struct Task* task) +{ + Scheduler_push_back(task->sched, *task); + return false; +} + + +static bool taskA(struct Task* task) +{ + cco_begin(task); + puts("Hello, from task A"); + cco_yield(resume_task(task)); + puts("A is back doing work"); + cco_yield(resume_task(task)); + puts("A is back doing more work"); + cco_yield(resume_task(task)); + puts("A is back doing even more work"); + cco_final: + cco_end(true); +} + +static bool taskB(struct Task* task) +{ + cco_begin(task); + puts("Hello, from task B"); + cco_yield(resume_task(task)); + puts("B is back doing work"); + cco_yield(resume_task(task)); + puts("B is back doing more work"); + cco_final: + cco_end(true); +} + +void Use(void) +{ + Scheduler scheduler = c_make(Scheduler, { + {taskA, &scheduler}, + {taskB, &scheduler}, + }); + + while (schedule(&scheduler)) {} + + Scheduler_drop(&scheduler); +} + +int main() +{ + Use(); +} diff --git a/misc/examples/triples.c b/misc/examples/triples.c index 520bf012..2e0211c3 100644 --- a/misc/examples/triples.c +++ b/misc/examples/triples.c @@ -23,13 +23,13 @@ struct triples { int cco_state; }; -bool triples_next(struct triples* I) { +bool triples_coro(struct triples* I) { cco_begin(I); for (I->c = 5; I->n; ++I->c) { for (I->a = 1; I->a < I->c; ++I->a) { for (I->b = I->a + 1; I->b < I->c; ++I->b) { if ((int64_t)I->a*I->a + (int64_t)I->b*I->b == (int64_t)I->c*I->c) { - cco_yield(true); + cco_yield(false); if (--I->n == 0) cco_return; } } @@ -37,7 +37,7 @@ bool triples_next(struct triples* I) { } cco_final: puts("done"); - cco_end(false); + cco_end(true); } int gcd(int a, int b) { @@ -58,7 +58,7 @@ int main() struct triples t = {INT32_MAX}; int n = 0; - while (triples_next(&t)) { + while (!triples_coro(&t)) { if (gcd(t.a, t.b) > 1) continue; if (t.c < 100) |
