summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorTyge Løvset <[email protected]>2023-08-01 16:47:38 +0200
committerTyge Løvset <[email protected]>2023-08-01 16:47:38 +0200
commit2ae41a5b709abdb549c4128b43e25ee8a704fcc4 (patch)
tree8494d338510f033b95c3ed0c5f3abebaa080dfd5
parent6354a597892e84baa6c3a99b98f2c7acaf33a99d (diff)
downloadSTC-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.md47
-rw-r--r--include/stc/coroutine.h46
-rw-r--r--misc/examples/coroutines/cointerleave.c2
-rw-r--r--misc/examples/coroutines/coread.c2
-rw-r--r--misc/examples/coroutines/coroutines.c8
-rw-r--r--misc/examples/coroutines/cotasks1.c4
-rw-r--r--misc/examples/coroutines/cotasks2.c10
-rw-r--r--misc/examples/coroutines/dining_philosophers.c8
-rw-r--r--misc/examples/coroutines/filetask.c6
-rw-r--r--misc/examples/coroutines/triples.c2
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)