diff options
| author | Tyge Løvset <[email protected]> | 2023-08-01 16:47:38 +0200 |
|---|---|---|
| committer | Tyge Løvset <[email protected]> | 2023-08-01 16:47:38 +0200 |
| commit | 2ae41a5b709abdb549c4128b43e25ee8a704fcc4 (patch) | |
| tree | 8494d338510f033b95c3ed0c5f3abebaa080dfd5 | |
| parent | 6354a597892e84baa6c3a99b98f2c7acaf33a99d (diff) | |
| download | STC-modified-2ae41a5b709abdb549c4128b43e25ee8a704fcc4.tar.gz STC-modified-2ae41a5b709abdb549c4128b43e25ee8a704fcc4.zip | |
Last minute API change for coroutines before V4.3 release:
- Renamed cco_xxxx_await() => cco_await_xxxx() (call, task, sem, timer)
- Renamed cco_xxxx_blocking() => cco_blocking_xxxx() (call, task)
- Renamed cco_task_resume() => cco_resume_task()
| -rw-r--r-- | docs/coroutine_api.md | 47 | ||||
| -rw-r--r-- | include/stc/coroutine.h | 46 | ||||
| -rw-r--r-- | misc/examples/coroutines/cointerleave.c | 2 | ||||
| -rw-r--r-- | misc/examples/coroutines/coread.c | 2 | ||||
| -rw-r--r-- | misc/examples/coroutines/coroutines.c | 8 | ||||
| -rw-r--r-- | misc/examples/coroutines/cotasks1.c | 4 | ||||
| -rw-r--r-- | misc/examples/coroutines/cotasks2.c | 10 | ||||
| -rw-r--r-- | misc/examples/coroutines/dining_philosophers.c | 8 | ||||
| -rw-r--r-- | misc/examples/coroutines/filetask.c | 6 | ||||
| -rw-r--r-- | misc/examples/coroutines/triples.c | 2 |
10 files changed, 68 insertions, 67 deletions
diff --git a/docs/coroutine_api.md b/docs/coroutine_api.md index 30b85640..6dc12123 100644 --- a/docs/coroutine_api.md +++ b/docs/coroutine_api.md @@ -24,23 +24,24 @@ NB! ***cco_yield\*()*** / ***cco_await\*()*** may not be called from within a `s | | `cco_yield_final();` | Yield final suspend, enter cleanup-state | | | `cco_yield_final(ret);` | Yield a final value | | | `cco_await(condition);` | Suspend until condition is true (return CCO_AWAIT)| -| | `cco_call_await(cocall);` | Await for subcoro to finish (returns its ret value) | -| | `cco_call_await(cocall, retbit);` | Await for subcoro's return to be in (retbit \| CCO_DONE) | +| | `cco_await_call(cocall);` | Await for subcoro to finish (returns its ret value) | +| | `cco_await_call(cocall, retbit);` | Await for subcoro's return to be in (retbit \| CCO_DONE) | | | `cco_return;` | Return from coroutine (inside cco_routine) | | | Task objects: | | | | `cco_task_struct(Name, ...);` | Define a coroutine task struct | -| | `cco_task_await(task, cco_runtime* rt);`| Await for task to finish | -| | `cco_task_await(task, rt, retbit);` | Await for task's return to be in (retbit \| CCO_DONE) | -|`cco_result`| `cco_task_resume(task, rt);` | Resume suspended task | +| | `cco_await_task(task, cco_runtime* rt);`| Await for task to finish | +| | `cco_await_task(task, rt, retbit);` | Await for task's return to be in (retbit \| CCO_DONE) | +|`cco_result`| `cco_resume_task(task, rt);` | Resume suspended task | | | Semaphores: | | | | `cco_sem` | Semaphore type | +| | `cco_await_sem(sem)` | Await for the semaphore count > 0 | | `cco_sem` | `cco_sem_from(long value)` | Create semaphore | | | `cco_sem_set(sem, long value)` | Set semaphore value | -| | `cco_sem_await(sem)` | Await for the semaphore count > 0 | + | | `cco_sem_release(sem)` | Signal the semaphore (count += 1) | | | Timers: | | | | `cco_timer` | Timer type | -| | `cco_timer_await(tm, double sec)` | Await secs for timer to expire (usec prec.)| +| | `cco_await_timer(tm, double sec)` | Await secs for timer to expire (usec prec.)| | | `cco_timer_start(tm, double sec)` | Start timer for secs duration | | | `cco_timer_restart(tm)` | Restart timer with same duration | | `bool` | `cco_timer_expired(tm)` | Return true if timer is expired | @@ -49,10 +50,10 @@ NB! ***cco_yield\*()*** / ***cco_await\*()*** may not be called from within a `s | | From caller side: | | | `void` | `cco_stop(co)` | Next call of coroutine finalizes | | `void` | `cco_reset(co)` | Reset state to initial (for reuse) | -| `void` | `cco_call_blocking(cocall) {}` | Run blocking until cocall is finished | -| `void` | `cco_call_blocking(cocall, int* outres) {}`| Run blocking until cocall is finished | -| | `cco_task_blocking(task) {}` | Run blocking until task is finished | -| | `cco_task_blocking(task, rt, STACKSZ) {}`| Run blocking until task is finished | +| `void` | `cco_blocking_call(cocall) {}` | Run blocking until cocall is finished | +| `void` | `cco_blocking_call(cocall, int* outres) {}`| Run blocking until cocall is finished | +| | `cco_blocking_task(task) {}` | Run blocking until task is finished | +| | `cco_blocking_task(task, rt, STACKSZ) {}`| Run blocking until task is finished | | | Time functions: | | | `double` | `cco_time(void)` | Return secs with usec prec. since Epoch | | | `cco_sleep(double sec)` | Sleep for seconds (msec or usec prec.) | @@ -117,7 +118,7 @@ int main(void) { struct triples co = {.max_c = 25}; int n = 0; - cco_call_blocking(triples(&co)) { + cco_blocking_call(triples(&co)) { printf("%d: [%d, %d, %d]\n", ++n, co.a, co.b, co.c); } } @@ -171,26 +172,26 @@ int main(void) { struct gcd1_triples co = {.max_n = 100, .max_c = 100}; int n = 0; - cco_call_blocking(gcd1_triples(&co)) { + cco_blocking_call(gcd1_triples(&co)) { printf("%d: [%d, %d, %d]\n", ++n, co.tri.a, co.tri.b, co.tri.c); } } ``` -When using ***cco_call_blocking()***, the coroutine is continuously resumed after each yield suspension. +When using ***cco_blocking_call()***, the coroutine is continuously resumed after each yield suspension. However, this means that it first calls ***gcd1_triples()***, which immediately jumps to after the `cco_yield` -statement and calls ***triples()***, which again jumps and resumes after its `cco_yield`-statement. This is efficient only when yielding or awaiting from the top- or second-level call like here, but naturally not when couroutine calls are more deeply nested or recursive. The STC coroutine implementation therefore also contains task-objects (`cco_task`), which are base-coroutine -objects/enclosures. These can be executed using ***cco_task_blocking()*** instead of ***cco_call_blocking()***. -Inner coroutine calls are done by ***cco_task_await()***, where you may await for a certain return value, normally CCO_YIELD or just CCO_DONE. It uses a stack of pointers of task-enclosures to call the current +objects/enclosures. These can be executed using ***cco_blocking_task()*** instead of ***cco_blocking_call()***. +Inner coroutine calls are done by ***cco_await_task()***, where you may await for a certain return value, normally CCO_YIELD or just CCO_DONE. It uses a stack of pointers of task-enclosures to call the current inner-level function directly. The task-objects have the added benefit that coroutines can be managed by a scheduler, which is useful when dealing with large numbers of coroutines (like in many games and simulations). Note that these two modes may be mixed, and that for short-lived coroutines (only a few suspends), -it is often beneficial to call the sub-coroutine directly rather than via ***cco_task_await()*** +it is often beneficial to call the sub-coroutine directly rather than via ***cco_await_task()*** (which pushes the task on top of the runtime stack and yields - then executed on next resume). The following example uses task-objects with 3-levels deep coroutine calls. It emulates an async generator @@ -212,7 +213,7 @@ int next_value(struct next_value* co, cco_runtime* rt) { cco_routine (co) { while (true) { - cco_timer_await(&co->tm, 1 + rand() % 2); // suspend with CCO_AWAIT + cco_await_timer(&co->tm, 1 + rand() % 2); // suspend with CCO_AWAIT co->val = rand(); cco_yield(); // suspend with CCO_YIELD } @@ -242,7 +243,7 @@ int produce_items(struct produce_items* p, cco_runtime* rt) while (true) { // await for CCO_YIELD (or CCO_DONE) - cco_task_await(&p->next, rt, CCO_YIELD); + cco_await_task(&p->next, rt, CCO_YIELD); cstr_printf(&p->str, "item %d", p->next.val); print_time(); printf("produced %s\n", cstr_str(&p->str)); @@ -269,13 +270,13 @@ int consume_items(struct consume_items* c, cco_runtime* rt) for (c->i = 1; c->i <= c->n; ++c->i) { printf("consume #%d\n", c->i); - cco_task_await(&c->produce, rt, CCO_YIELD); + cco_await_task(&c->produce, rt, CCO_YIELD); print_time(); printf("consumed %s\n", cstr_str(&c->produce.str)); } cco_cleanup: cco_stop(&c->produce); - cco_task_resume(&c->produce, rt); + cco_resume_task(&c->produce, rt); puts("done consume"); } return 0; @@ -287,6 +288,6 @@ int main(void) .n = 5, .cco_func = consume_items, }; - cco_task_blocking(&consume); + cco_blocking_task(&consume); } -```
\ No newline at end of file +``` diff --git a/include/stc/coroutine.h b/include/stc/coroutine.h index 4a7fd6aa..01a32fc2 100644 --- a/include/stc/coroutine.h +++ b/include/stc/coroutine.h @@ -91,19 +91,19 @@ typedef enum { case __LINE__: if (!(promise)) {return ret; goto _resume;} \ } while (0) -/* cco_call_await(): assumes coroutine returns a cco_result value (int) */ -#define cco_call_await(...) c_MACRO_OVERLOAD(cco_call_await, __VA_ARGS__) -#define cco_call_await_1(corocall) cco_call_await_2(corocall, CCO_DONE) -#define cco_call_await_2(corocall, awaitbits) \ +/* cco_await_call(): assumes coroutine returns a cco_result value (int) */ +#define cco_await_call(...) c_MACRO_OVERLOAD(cco_await_call, __VA_ARGS__) +#define cco_await_call_1(corocall) cco_await_call_2(corocall, CCO_DONE) +#define cco_await_call_2(corocall, awaitbits) \ do { \ *_state = __LINE__; \ case __LINE__: { int _r = corocall; if (_r & ~(awaitbits)) {return _r; goto _resume;} } \ } while (0) -/* cco_call_blocking(): assumes coroutine returns a cco_result value (int) */ -#define cco_call_blocking(...) c_MACRO_OVERLOAD(cco_call_blocking, __VA_ARGS__) -#define cco_call_blocking_1(corocall) while ((corocall) != CCO_DONE) -#define cco_call_blocking_2(corocall, result) while ((*(result) = (corocall)) != CCO_DONE) +/* cco_blocking_call(): assumes coroutine returns a cco_result value (int) */ +#define cco_blocking_call(...) c_MACRO_OVERLOAD(cco_blocking_call, __VA_ARGS__) +#define cco_blocking_call_1(corocall) while ((corocall) != CCO_DONE) +#define cco_blocking_call_2(corocall, result) while ((*(result) = (corocall)) != CCO_DONE) #define cco_cleanup \ *_state = CCO_STATE_CLEANUP; case CCO_STATE_CLEANUP @@ -165,23 +165,23 @@ typedef struct cco_runtime { #define cco_cast_task(task) \ ((cco_task *)(task) + 0*sizeof((task)->cco_func(task, (cco_runtime*)0) + ((int*)0 == &(task)->cco_state))) -#define cco_task_resume(task, rt) \ +#define cco_resume_task(task, rt) \ (task)->cco_func(task, rt) -#define cco_task_await(...) c_MACRO_OVERLOAD(cco_task_await, __VA_ARGS__) -#define cco_task_await_2(task, rt) cco_task_await_3(task, rt, CCO_DONE) -#define cco_task_await_3(task, rt, awaitbits) \ +#define cco_await_task(...) c_MACRO_OVERLOAD(cco_await_task, __VA_ARGS__) +#define cco_await_task_2(task, rt) cco_await_task_3(task, rt, CCO_DONE) +#define cco_await_task_3(task, rt, awaitbits) \ do { \ cco_runtime* _rt = rt; \ (_rt->stack[++_rt->top] = cco_cast_task(task))->cco_expect = (awaitbits); \ cco_yield_v(CCO_AWAIT); \ } while (0) -#define cco_task_blocking(...) c_MACRO_OVERLOAD(cco_task_blocking, __VA_ARGS__) -#define cco_task_blocking_1(task) cco_task_blocking_3(task, _rt, 16) -#define cco_task_blocking_3(task, rt, STACKDEPTH) \ +#define cco_blocking_task(...) c_MACRO_OVERLOAD(cco_blocking_task, __VA_ARGS__) +#define cco_blocking_task_1(task) cco_blocking_task_3(task, _rt, 16) +#define cco_blocking_task_3(task, rt, STACKDEPTH) \ for (struct { int result, top; cco_task* stack[STACKDEPTH]; } rt = {.stack={cco_cast_task(task)}}; \ - (((rt.result = cco_task_resume(rt.stack[rt.top], (cco_runtime*)&rt)) & \ + (((rt.result = cco_resume_task(rt.stack[rt.top], (cco_runtime*)&rt)) & \ ~rt.stack[rt.top]->cco_expect) || --rt.top >= 0); ) /* @@ -190,9 +190,9 @@ typedef struct cco_runtime { typedef struct { intptr_t count; } cco_sem; -#define cco_sem_await(sem) cco_sem_await_and_return(sem, CCO_AWAIT) -#define cco_sem_await_v(sem) cco_sem_await_and_return(sem, ) -#define cco_sem_await_and_return(sem, ret) \ +#define cco_await_sem(sem) cco_await_sem_and_return(sem, CCO_AWAIT) +#define cco_await_sem_v(sem) cco_await_sem_and_return(sem, ) +#define cco_await_sem_and_return(sem, ret) \ do { \ cco_await_and_return((sem)->count > 0, ret); \ --(sem)->count; \ @@ -248,10 +248,10 @@ typedef struct { intptr_t count; } cco_sem; typedef struct { double interval, start; } cco_timer; -#define cco_timer_await(tm, sec) cco_timer_await_v_3(tm, sec, CCO_AWAIT) -#define cco_timer_await_v(...) c_MACRO_OVERLOAD(cco_timer_await_v, __VA_ARGS__) -#define cco_timer_await_v_2(tm, sec) cco_timer_await_v_3(tm, sec, ) -#define cco_timer_await_v_3(tm, sec, ret) \ +#define cco_await_timer(tm, sec) cco_await_timer_v_3(tm, sec, CCO_AWAIT) +#define cco_await_timer_v(...) c_MACRO_OVERLOAD(cco_await_timer_v, __VA_ARGS__) +#define cco_await_timer_v_2(tm, sec) cco_await_timer_v_3(tm, sec, ) +#define cco_await_timer_v_3(tm, sec, ret) \ do { \ cco_timer_start(tm, sec); \ cco_await_and_return(cco_timer_expired(tm), ret); \ diff --git a/misc/examples/coroutines/cointerleave.c b/misc/examples/coroutines/cointerleave.c index f3710ba3..80494176 100644 --- a/misc/examples/coroutines/cointerleave.c +++ b/misc/examples/coroutines/cointerleave.c @@ -49,7 +49,7 @@ void Use(void) struct Generator g = {{&a}, {&b}}; - cco_call_blocking(interleaved(&g)) { + cco_blocking_call(interleaved(&g)) { printf("%d ", g.value); } puts(""); diff --git a/misc/examples/coroutines/coread.c b/misc/examples/coroutines/coread.c index ebaaf19d..359ca85d 100644 --- a/misc/examples/coroutines/coread.c +++ b/misc/examples/coroutines/coread.c @@ -33,7 +33,7 @@ int main(void) { struct file_read g = {__FILE__}; int n = 0; - cco_call_blocking(file_read(&g)) + cco_blocking_call(file_read(&g)) { printf("%3d %s\n", ++n, cstr_str(&g.line)); //if (n == 10) cco_stop(&g); diff --git a/misc/examples/coroutines/coroutines.c b/misc/examples/coroutines/coroutines.c index 489c3ed6..e642a823 100644 --- a/misc/examples/coroutines/coroutines.c +++ b/misc/examples/coroutines/coroutines.c @@ -84,13 +84,13 @@ struct combined { int combined(struct combined* g) { cco_routine(g) { - cco_call_await(prime(&g->prm)); - cco_call_await(fibonacci(&g->fib)); + cco_await_call(prime(&g->prm)); + cco_await_call(fibonacci(&g->fib)); // Reuse the g->prm context and extend the count: g->prm.count = 8, g->prm.result += 2; cco_reset(&g->prm); - cco_call_await(prime(&g->prm)); + cco_await_call(prime(&g->prm)); cco_cleanup: puts("final combined"); @@ -103,7 +103,7 @@ int main(void) struct combined c = {.prm={.count=8}, .fib={14}}; int res; - cco_call_blocking(combined(&c), &res) { + cco_blocking_call(combined(&c), &res) { if (res == CCO_YIELD) printf("Prime(%d)=%lld, Fib(%d)=%lld\n", c.prm.idx, c.prm.result, diff --git a/misc/examples/coroutines/cotasks1.c b/misc/examples/coroutines/cotasks1.c index e4afbe2b..230bd62b 100644 --- a/misc/examples/coroutines/cotasks1.c +++ b/misc/examples/coroutines/cotasks1.c @@ -16,7 +16,7 @@ int next_value(struct next_value* co) { cco_routine (co) { while (true) { - cco_timer_await(&co->tm, 1 + rand() % 2); + cco_await_timer(&co->tm, 1 + rand() % 2); co->val = rand(); cco_yield(); } @@ -88,7 +88,7 @@ int main(void) struct consume_items consume = {.n=5}; int count = 0; - cco_call_blocking(consume_items(&consume, &produce)) + cco_blocking_call(consume_items(&consume, &produce)) { ++count; //cco_sleep(0.001); diff --git a/misc/examples/coroutines/cotasks2.c b/misc/examples/coroutines/cotasks2.c index 4fdf98d9..d77a28bc 100644 --- a/misc/examples/coroutines/cotasks2.c +++ b/misc/examples/coroutines/cotasks2.c @@ -15,7 +15,7 @@ int next_value(struct next_value* co, cco_runtime* rt) { cco_routine (co) { while (true) { - cco_timer_await(&co->tm, 1 + rand() % 2); + cco_await_timer(&co->tm, 1 + rand() % 2); co->val = rand(); cco_yield(); } @@ -46,7 +46,7 @@ int produce_items(struct produce_items* p, cco_runtime* rt) while (true) { // await for next CCO_YIELD (or CCO_DONE) in next_value - cco_task_await(&p->next, rt, CCO_YIELD); + cco_await_task(&p->next, rt, CCO_YIELD); cstr_printf(&p->str, "item %d", p->next.val); print_time(); printf("produced %s\n", cstr_str(&p->str)); @@ -75,14 +75,14 @@ int consume_items(struct consume_items* c, cco_runtime* rt) for (c->i = 1; c->i <= c->n; ++c->i) { printf("consume #%d\n", c->i); - cco_task_await(&c->produce, rt, CCO_YIELD); + cco_await_task(&c->produce, rt, CCO_YIELD); print_time(); printf("consumed %s\n", cstr_str(&c->produce.str)); } cco_cleanup: cco_stop(&c->produce); - cco_task_resume(&c->produce, rt); + cco_resume_task(&c->produce, rt); puts("done consume"); } return 0; @@ -94,5 +94,5 @@ int main(void) .cco_func = consume_items, .n = 5, }; - cco_task_blocking(&consume); + cco_blocking_task(&consume); } diff --git a/misc/examples/coroutines/dining_philosophers.c b/misc/examples/coroutines/dining_philosophers.c index abe09204..f7edf815 100644 --- a/misc/examples/coroutines/dining_philosophers.c +++ b/misc/examples/coroutines/dining_philosophers.c @@ -34,15 +34,15 @@ int philosopher(struct Philosopher* p) while (1) { duration = 1.0 + crandf()*2.0; printf("Philosopher %d is thinking for %.0f minutes...\n", p->id, duration*10); - cco_timer_await(&p->tm, duration); + cco_await_timer(&p->tm, duration); printf("Philosopher %d is hungry...\n", p->id); - cco_sem_await(p->left_fork); - cco_sem_await(p->right_fork); + cco_await_sem(p->left_fork); + cco_await_sem(p->right_fork); duration = 0.5 + crandf(); printf("Philosopher %d is eating for %.0f minutes...\n", p->id, duration*10); - cco_timer_await(&p->tm, duration); + cco_await_timer(&p->tm, duration); cco_sem_release(p->left_fork); cco_sem_release(p->right_fork); diff --git a/misc/examples/coroutines/filetask.c b/misc/examples/coroutines/filetask.c index bfce7810..28292801 100644 --- a/misc/examples/coroutines/filetask.c +++ b/misc/examples/coroutines/filetask.c @@ -21,7 +21,7 @@ int file_read(struct file_read* co, cco_runtime* rt) while (true) { // emulate async io: await 10ms per line - cco_timer_await(&co->tm, 0.003); + cco_await_timer(&co->tm, 0.003); if (!cstr_getline(&co->line, co->fp)) break; @@ -50,7 +50,7 @@ int count_line(struct count_line* co, cco_runtime* rt) while (true) { // await for next CCO_YIELD (or CCO_DONE) in file_read() - cco_task_await(&co->reader, rt, CCO_YIELD); + cco_await_task(&co->reader, rt, CCO_YIELD); if (rt->result == CCO_DONE) break; co->lineCount += 1; cco_yield(); @@ -73,7 +73,7 @@ int main(void) // Execute coroutine as top-level blocking int loop = 0; - cco_task_blocking(&countTask) { ++loop; } + cco_blocking_task(&countTask) { ++loop; } printf("line count = %d\n", countTask.lineCount); printf("exec count = %d\n", loop); diff --git a/misc/examples/coroutines/triples.c b/misc/examples/coroutines/triples.c index 9fd771ce..22914c2b 100644 --- a/misc/examples/coroutines/triples.c +++ b/misc/examples/coroutines/triples.c @@ -64,7 +64,7 @@ int main(void) struct triples t = {.max_c = 100}; int n = 0; - cco_call_blocking(triples_coro(&t)) { + cco_blocking_call(triples_coro(&t)) { if (gcd(t.a, t.b) > 1) continue; if (++n <= 20) |
