summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--docs/ccommon_api.md28
-rw-r--r--include/stc/algo/coroutine.h49
-rw-r--r--misc/examples/cointerleave.c60
-rw-r--r--misc/examples/coread.c14
-rw-r--r--misc/examples/coroutines.c20
-rw-r--r--misc/examples/scheduler.c76
-rw-r--r--misc/examples/triples.c8
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)