summaryrefslogtreecommitdiffhomepage
path: root/misc/examples/coroutines
diff options
context:
space:
mode:
Diffstat (limited to 'misc/examples/coroutines')
-rw-r--r--misc/examples/coroutines/cointerleave.c62
-rw-r--r--misc/examples/coroutines/coread.c41
-rw-r--r--misc/examples/coroutines/coroutines.c112
-rw-r--r--misc/examples/coroutines/cotasks1.c100
-rw-r--r--misc/examples/coroutines/cotasks2.c98
-rw-r--r--misc/examples/coroutines/dining_philosophers.c105
-rw-r--r--misc/examples/coroutines/filetask.c80
-rw-r--r--misc/examples/coroutines/generator.c66
-rw-r--r--misc/examples/coroutines/scheduler.c67
-rw-r--r--misc/examples/coroutines/triples.c75
10 files changed, 806 insertions, 0 deletions
diff --git a/misc/examples/coroutines/cointerleave.c b/misc/examples/coroutines/cointerleave.c
new file mode 100644
index 00000000..80494176
--- /dev/null
+++ b/misc/examples/coroutines/cointerleave.c
@@ -0,0 +1,62 @@
+// https://www.youtube.com/watch?v=8sEe-4tig_A
+#include <stdio.h>
+#include <stc/coroutine.h>
+#define i_type IVec
+#define i_key int
+#include <stc/cvec.h>
+
+struct GenValue {
+ IVec *v;
+ IVec_iter it;
+ int cco_state;
+};
+
+static int get_value(struct GenValue* g)
+{
+ cco_routine(g) {
+ for (g->it = IVec_begin(g->v); g->it.ref; IVec_next(&g->it))
+ cco_yield_v(*g->it.ref);
+ }
+ return -1;
+}
+
+struct Generator {
+ struct GenValue x, y;
+ int cco_state;
+ int value;
+};
+
+cco_result interleaved(struct Generator* g)
+{
+ cco_routine(g) {
+ while (!(cco_done(&g->x) & cco_done(&g->y))) {
+ g->value = get_value(&g->x);
+ if (!cco_done(&g->x))
+ cco_yield();
+
+ g->value = get_value(&g->y);
+ if (!cco_done(&g->y))
+ cco_yield();
+ }
+ }
+ return CCO_DONE;
+}
+
+void Use(void)
+{
+ IVec a = c_init(IVec, {2, 4, 6, 8, 10, 11});
+ IVec b = c_init(IVec, {3, 5, 7, 9});
+
+ struct Generator g = {{&a}, {&b}};
+
+ cco_blocking_call(interleaved(&g)) {
+ printf("%d ", g.value);
+ }
+ puts("");
+ c_drop(IVec, &a, &b);
+}
+
+int main(void)
+{
+ Use();
+}
diff --git a/misc/examples/coroutines/coread.c b/misc/examples/coroutines/coread.c
new file mode 100644
index 00000000..6d3acdd7
--- /dev/null
+++ b/misc/examples/coroutines/coread.c
@@ -0,0 +1,41 @@
+#define i_implement
+#include <stc/cstr.h>
+#include <stc/coroutine.h>
+#include <errno.h>
+
+// Read file line by line using coroutines:
+
+struct file_read {
+ const char* filename;
+ int cco_state;
+ FILE* fp;
+ cstr line;
+};
+
+int file_read(struct file_read* g)
+{
+ cco_routine(g) {
+ g->fp = fopen(g->filename, "r");
+ if (!g->fp) cco_return;
+ g->line = cstr_init();
+
+ cco_await(!cstr_getline(&g->line, g->fp));
+
+ cco_final:
+ printf("finish\n");
+ cstr_drop(&g->line);
+ if (g->fp) fclose(g->fp);
+ }
+ return 0;
+}
+
+int main(void)
+{
+ struct file_read g = {__FILE__};
+ int n = 0;
+ 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
new file mode 100644
index 00000000..802a976a
--- /dev/null
+++ b/misc/examples/coroutines/coroutines.c
@@ -0,0 +1,112 @@
+#include <stc/coroutine.h>
+#include <stdio.h>
+#include <stdint.h>
+
+// Demonstrate to call another coroutine from a coroutine:
+// First create prime generator, then call fibonacci sequence:
+
+bool is_prime(long long i) {
+ for (long long j=2; j*j <= i; ++j)
+ if (i % j == 0) return false;
+ return true;
+}
+
+struct prime {
+ int count, idx;
+ long long result, pos;
+ int cco_state;
+};
+
+int prime(struct prime* g) {
+ cco_routine(g) {
+ if (g->result < 2) g->result = 2;
+ if (g->result == 2) {
+ if (g->count-- == 0) cco_return;
+ ++g->idx;
+ cco_yield();
+ }
+ g->result += !(g->result & 1);
+ for (g->pos = g->result; g->count > 0; g->pos += 2) {
+ if (is_prime(g->pos)) {
+ --g->count;
+ ++g->idx;
+ g->result = g->pos;
+ cco_yield();
+ }
+ }
+ cco_final:
+ printf("final prm\n");
+ }
+ return 0;
+}
+
+
+// Use coroutine to create a fibonacci sequence generator:
+
+struct fibonacci {
+ int count, idx;
+ long long result, b;
+ int cco_state;
+};
+
+int fibonacci(struct fibonacci* g) {
+ assert(g->count < 94);
+
+ long long sum;
+ cco_routine(g) {
+ g->idx = 0;
+ g->result = 0;
+ g->b = 1;
+ for (;;) {
+ if (g->count-- == 0)
+ cco_return;
+ if (++g->idx > 1) {
+ // NB! locals lasts only until next yield/await!
+ sum = g->result + g->b;
+ g->result = g->b;
+ g->b = sum;
+ }
+ cco_yield();
+ }
+ cco_final:
+ printf("final fib\n");
+ }
+ return 0;
+}
+
+// Combine
+
+struct combined {
+ struct prime prm;
+ struct fibonacci fib;
+ int cco_state;
+};
+
+int combined(struct combined* g) {
+ cco_routine(g) {
+ 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_await_call(prime(&g->prm));
+
+ cco_final:
+ puts("final combined");
+ }
+ return 0;
+}
+
+int main(void)
+{
+ struct combined c = {.prm={.count=8}, .fib={14}};
+ int res;
+
+ cco_blocking_call(res = combined(&c)) {
+ if (res == CCO_YIELD)
+ printf("Prime(%d)=%lld, Fib(%d)=%lld\n",
+ c.prm.idx, c.prm.result,
+ c.fib.idx, c.fib.result);
+ }
+}
diff --git a/misc/examples/coroutines/cotasks1.c b/misc/examples/coroutines/cotasks1.c
new file mode 100644
index 00000000..cffd6620
--- /dev/null
+++ b/misc/examples/coroutines/cotasks1.c
@@ -0,0 +1,100 @@
+// https://mariusbancila.ro/blog/2020/06/22/a-cpp20-coroutine-example/
+
+#include <time.h>
+#include <stdio.h>
+#define i_static
+#include <stc/cstr.h>
+#include <stc/coroutine.h>
+
+struct next_value {
+ int val;
+ int cco_state;
+ cco_timer tm;
+};
+
+int next_value(struct next_value* co)
+{
+ cco_routine (co) {
+ while (true) {
+ cco_await_timer(&co->tm, 1 + rand() % 2);
+ co->val = rand();
+ cco_yield();
+ }
+ }
+ return 0;
+}
+
+void print_time()
+{
+ time_t now = time(NULL);
+ char mbstr[64];
+ strftime(mbstr, sizeof(mbstr), "[%H:%M:%S]", localtime(&now));
+ printf("%s ", mbstr);
+}
+
+// PRODUCER
+
+struct produce_items {
+ struct next_value next;
+ cstr text;
+ int cco_state;
+};
+
+int produce_items(struct produce_items* p)
+{
+ cco_routine (p) {
+ p->text = cstr_init();
+ while (true)
+ {
+ cco_await(next_value(&p->next) != CCO_AWAIT);
+ cstr_printf(&p->text, "item %d", p->next.val);
+ print_time();
+ printf("produced %s\n", cstr_str(&p->text));
+ cco_yield();
+ }
+ cco_final:
+ cstr_drop(&p->text);
+ puts("done produce");
+ }
+ return 0;
+}
+
+// CONSUMER
+
+struct consume_items {
+ int n, i;
+ int cco_state;
+};
+
+int consume_items(struct consume_items* c, struct produce_items* p)
+{
+ cco_routine (c) {
+ for (c->i = 1; c->i <= c->n; ++c->i)
+ {
+ printf("consume #%d\n", c->i);
+ cco_await(produce_items(p) != CCO_AWAIT);
+ print_time();
+ printf("consumed %s\n", cstr_str(&p->text));
+ }
+ cco_final:
+ puts("done consume");
+ }
+ return 0;
+}
+
+int main(void)
+{
+ struct produce_items produce = {0};
+ struct consume_items consume = {.n=5};
+ int count = 0;
+
+ cco_blocking_call(consume_items(&consume, &produce))
+ {
+ ++count;
+ //cco_sleep(0.001);
+ //if (consume.i == 3) cco_stop(&consume);
+ }
+ cco_stop(&produce);
+ produce_items(&produce);
+ printf("count: %d\n", count);
+} \ No newline at end of file
diff --git a/misc/examples/coroutines/cotasks2.c b/misc/examples/coroutines/cotasks2.c
new file mode 100644
index 00000000..558df118
--- /dev/null
+++ b/misc/examples/coroutines/cotasks2.c
@@ -0,0 +1,98 @@
+// https://mariusbancila.ro/blog/2020/06/22/a-cpp20-coroutine-example/
+
+#include <time.h>
+#include <stdio.h>
+#define i_static
+#include <stc/cstr.h>
+#include <stc/coroutine.h>
+
+cco_task_struct (next_value,
+ int val;
+ cco_timer tm;
+);
+
+int next_value(struct next_value* co, cco_runtime* rt)
+{
+ cco_routine (co) {
+ while (true) {
+ cco_await_timer(&co->tm, 1 + rand() % 2);
+ co->val = rand();
+ cco_yield();
+ }
+ }
+ return 0;
+}
+
+void print_time()
+{
+ time_t now = time(NULL);
+ char mbstr[64];
+ strftime(mbstr, sizeof(mbstr), "[%H:%M:%S]", localtime(&now));
+ printf("%s ", mbstr);
+}
+
+// PRODUCER
+
+cco_task_struct (produce_items,
+ struct next_value next;
+ cstr text;
+);
+
+int produce_items(struct produce_items* p, cco_runtime* rt)
+{
+ cco_routine (p) {
+ p->text = cstr_init();
+ p->next.cco_func = next_value;
+ while (true)
+ {
+ // await for next CCO_YIELD (or CCO_DONE) in next_value
+ cco_await_task(&p->next, rt, CCO_YIELD);
+ cstr_printf(&p->text, "item %d", p->next.val);
+ print_time();
+ printf("produced %s\n", cstr_str(&p->text));
+ cco_yield();
+ }
+
+ cco_final:
+ cstr_drop(&p->text);
+ puts("done produce");
+ }
+ return 0;
+}
+
+// CONSUMER
+
+cco_task_struct (consume_items,
+ int n, i;
+ struct produce_items produce;
+);
+
+int consume_items(struct consume_items* c, cco_runtime* rt)
+{
+ cco_routine (c) {
+ c->produce.cco_func = produce_items;
+
+ for (c->i = 1; c->i <= c->n; ++c->i)
+ {
+ printf("consume #%d\n", c->i);
+ cco_await_task(&c->produce, rt, CCO_YIELD);
+ print_time();
+ printf("consumed %s\n", cstr_str(&c->produce.text));
+ }
+
+ cco_final:
+ cco_stop(&c->produce);
+ cco_resume_task(&c->produce, rt);
+ puts("done consume");
+ }
+ return 0;
+}
+
+int main(void)
+{
+ struct consume_items consume = {
+ .cco_func = consume_items,
+ .n = 5,
+ };
+ cco_blocking_task(&consume);
+}
diff --git a/misc/examples/coroutines/dining_philosophers.c b/misc/examples/coroutines/dining_philosophers.c
new file mode 100644
index 00000000..d353b3b9
--- /dev/null
+++ b/misc/examples/coroutines/dining_philosophers.c
@@ -0,0 +1,105 @@
+// https://en.wikipedia.org/wiki/Dining_philosophers_problem
+#include <stdio.h>
+#include <time.h>
+#include <stc/crand.h>
+#include <stc/coroutine.h>
+
+// Define the number of philosophers and forks
+enum {
+ num_philosophers = 5,
+ num_forks = num_philosophers,
+};
+
+struct Philosopher {
+ int id;
+ cco_timer tm;
+ cco_sem* left_fork;
+ cco_sem* right_fork;
+ int cco_state; // required
+};
+
+struct Dining {
+ // Define semaphores for the forks
+ cco_sem forks[num_forks];
+ struct Philosopher ph[num_philosophers];
+ int cco_state; // required
+};
+
+
+// Philosopher coroutine
+int philosopher(struct Philosopher* p)
+{
+ double duration;
+ cco_routine(p) {
+ while (1) {
+ duration = 1.0 + crandf()*2.0;
+ printf("Philosopher %d is thinking for %.0f minutes...\n", p->id, duration*10);
+ cco_await_timer(&p->tm, duration);
+
+ printf("Philosopher %d is hungry...\n", p->id);
+ 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_await_timer(&p->tm, duration);
+
+ cco_sem_release(p->left_fork);
+ cco_sem_release(p->right_fork);
+ }
+
+ cco_final:
+ printf("Philosopher %d finished\n", p->id);
+ }
+ return 0;
+}
+
+
+// Dining coroutine
+int dining(struct Dining* d)
+{
+ cco_routine(d) {
+ for (int i = 0; i < num_forks; ++i)
+ cco_sem_set(&d->forks[i], 1); // all forks available
+ for (int i = 0; i < num_philosophers; ++i) {
+ cco_reset(&d->ph[i]);
+ d->ph[i].id = i + 1;
+ d->ph[i].left_fork = &d->forks[i];
+ d->ph[i].right_fork = &d->forks[(i + 1) % num_forks];
+ }
+
+ while (1) {
+ // per-"frame" logic resume each philosopher
+ for (int i = 0; i < num_philosophers; ++i) {
+ philosopher(&d->ph[i]);
+ }
+ cco_yield(); // suspend, return control back to main
+ }
+
+ cco_final:
+ for (int i = 0; i < num_philosophers; ++i) {
+ cco_stop(&d->ph[i]);
+ philosopher(&d->ph[i]);
+ }
+ puts("Dining finished");
+ }
+ return 0;
+}
+
+int main(void)
+{
+ struct Dining dine;
+ cco_reset(&dine);
+ int n=0;
+ cco_timer tm = cco_timer_from(15.0); // seconds
+ csrand((uint64_t)time(NULL));
+
+ cco_blocking_call(dining(&dine))
+ {
+ if (cco_timer_expired(&tm))
+ cco_stop(&dine);
+ cco_sleep(0.001);
+ ++n;
+ }
+ printf("n=%d\n", n);
+}
diff --git a/misc/examples/coroutines/filetask.c b/misc/examples/coroutines/filetask.c
new file mode 100644
index 00000000..9650cb60
--- /dev/null
+++ b/misc/examples/coroutines/filetask.c
@@ -0,0 +1,80 @@
+// https://github.com/lewissbaker/cppcoro#taskt
+
+#include <time.h>
+#include <stdio.h>
+#define i_static
+#include <stc/cstr.h>
+#include <stc/coroutine.h>
+
+cco_task_struct(file_read,
+ const char* path;
+ cstr line;
+ FILE* fp;
+ cco_timer tm;
+);
+
+int file_read(struct file_read* co, cco_runtime* rt)
+{
+ cco_routine (co) {
+ co->fp = fopen(co->path, "r");
+ co->line = cstr_init();
+
+ while (true) {
+ // emulate async io: await 10ms per line
+ cco_await_timer(&co->tm, 0.010);
+
+ if (!cstr_getline(&co->line, co->fp))
+ break;
+ cco_yield();
+ }
+
+ cco_final:
+ fclose(co->fp);
+ cstr_drop(&co->line);
+ puts("done file_read");
+ }
+ return 0;
+}
+
+cco_task_struct(count_line,
+ cstr path;
+ struct file_read reader;
+ int lineCount;
+);
+
+int count_line(struct count_line* co, cco_runtime* rt)
+{
+ cco_routine (co) {
+ co->reader.cco_func = file_read;
+ co->reader.path = cstr_str(&co->path);
+ while (true)
+ {
+ // await for next CCO_YIELD (or CCO_DONE) in file_read()
+ cco_await_task(&co->reader, rt, CCO_YIELD);
+ if (rt->result == CCO_DONE) break;
+ co->lineCount += 1;
+ cco_yield();
+ }
+
+ cco_final:
+ cstr_drop(&co->path);
+ puts("done count_line");
+ }
+ return 0;
+}
+
+int main(void)
+{
+ // Creates a new task
+ struct count_line countTask = {
+ .cco_func = count_line,
+ .path = cstr_from(__FILE__),
+ };
+
+ // Execute coroutine as top-level blocking
+ int loop = 0;
+ cco_blocking_task(&countTask) { ++loop; }
+
+ printf("line count = %d\n", countTask.lineCount);
+ printf("exec count = %d\n", loop);
+}
diff --git a/misc/examples/coroutines/generator.c b/misc/examples/coroutines/generator.c
new file mode 100644
index 00000000..96498498
--- /dev/null
+++ b/misc/examples/coroutines/generator.c
@@ -0,0 +1,66 @@
+// https://quuxplusone.github.io/blog/2019/03/06/pythagorean-triples/
+
+#include <stdio.h>
+#include <stc/coroutine.h>
+#include <stc/algorithm.h>
+
+typedef struct {
+ int max_triples;
+ int a, b, c;
+} Triple;
+
+// Create an iterable generator over Triple with count items.
+// Requires coroutine Triple_next() and function Triple_begin() to be defined.
+cco_iter_struct(Triple,
+ int count;
+);
+
+int Triple_next(Triple_iter* it) {
+ Triple* g = it->ref; // note: before cco_routine
+ cco_routine(it)
+ {
+ for (g->c = 5;; ++g->c) {
+ for (g->a = 1; g->a < g->c; ++g->a) {
+ for (g->b = g->a; g->b < g->c; ++g->b) {
+ if (g->a*g->a + g->b*g->b == g->c*g->c) {
+ if (it->count++ == g->max_triples)
+ cco_return;
+ cco_yield();
+ }
+ }
+ }
+ }
+ cco_final:
+ it->ref = NULL; // stop the iterator
+ }
+ return 0;
+}
+
+Triple_iter Triple_begin(Triple* g) {
+ Triple_iter it = {.ref=g};
+ Triple_next(&it);
+ return it;
+}
+
+
+int main(void)
+{
+ puts("Pythagorean triples.\nGet max 200 triples with c < 50:");
+ Triple triple = {.max_triples=200};
+
+ c_foreach (i, Triple, triple) {
+ if (i.ref->c < 50)
+ printf("%u: (%d, %d, %d)\n", i.count, i.ref->a, i.ref->b, i.ref->c);
+ else
+ cco_stop(&i);
+ }
+
+ puts("\nGet the 10 first triples with odd a's and a <= 20:");
+ c_forfilter (i, Triple, triple,
+ i.ref->a <= 20 &&
+ (i.ref->a & 1) &&
+ c_flt_take(i, 10)
+ ){
+ printf("%d: (%d, %d, %d)\n", c_flt_getcount(i), i.ref->a, i.ref->b, i.ref->c);
+ }
+}
diff --git a/misc/examples/coroutines/scheduler.c b/misc/examples/coroutines/scheduler.c
new file mode 100644
index 00000000..be1810d2
--- /dev/null
+++ b/misc/examples/coroutines/scheduler.c
@@ -0,0 +1,67 @@
+// https://www.youtube.com/watch?v=8sEe-4tig_A
+#include <stdio.h>
+#include <stc/coroutine.h>
+
+#define i_type cco_tasks
+#define i_key cco_task*
+#define i_keydrop(x) { puts("free task"); free(*x); }
+#define i_no_clone
+#include <stc/cqueue.h>
+
+typedef struct {
+ cco_tasks tasks;
+} cco_scheduler;
+
+void cco_scheduler_drop(cco_scheduler* sched) {
+ cco_tasks_drop(&sched->tasks);
+}
+
+int cco_scheduler_run(cco_scheduler* sched) {
+ while (!cco_tasks_empty(&sched->tasks)) {
+ cco_task* task = cco_tasks_pull(&sched->tasks);
+ if (cco_resume_task(task, NULL))
+ cco_tasks_push(&sched->tasks, task);
+ else
+ cco_tasks_value_drop(&task);
+ }
+ return 0;
+}
+
+static int taskA(cco_task* task, cco_runtime* rt) {
+ cco_routine(task) {
+ puts("Hello, from task A");
+ cco_yield();
+ puts("A is back doing work");
+ cco_yield();
+ puts("A is back doing more work");
+ cco_yield();
+ puts("A is back doing even more work");
+ }
+ return 0;
+}
+
+static int taskB(cco_task* task, cco_runtime* rt) {
+ cco_routine(task) {
+ puts("Hello, from task B");
+ cco_yield();
+ puts("B is back doing work");
+ cco_yield();
+ puts("B is back doing more work");
+ }
+ return 0;
+}
+
+void Use(void) {
+ cco_scheduler sched = {.tasks = c_init(cco_tasks, {
+ c_new(cco_task, {.cco_func=taskA}),
+ c_new(cco_task, {.cco_func=taskB}),
+ })};
+
+ cco_scheduler_run(&sched);
+ cco_scheduler_drop(&sched);
+}
+
+int main(void)
+{
+ Use();
+}
diff --git a/misc/examples/coroutines/triples.c b/misc/examples/coroutines/triples.c
new file mode 100644
index 00000000..d6ce2791
--- /dev/null
+++ b/misc/examples/coroutines/triples.c
@@ -0,0 +1,75 @@
+// https://quuxplusone.github.io/blog/2019/03/06/pythagorean-triples/
+
+#include <stdio.h>
+#include <stc/coroutine.h>
+
+void triples_vanilla(int max_c) {
+ for (int c = 5, i = 0;; ++c) {
+ for (int a = 1; a < c; ++a) {
+ for (int b = a + 1; b < c; ++b) {
+ if ((int64_t)a*a + (int64_t)b*b == (int64_t)c*c) {
+ if (c > max_c)
+ goto done;
+ printf("%d: {%d, %d, %d}\n", ++i, a, b, c);
+ }
+ }
+ }
+ }
+ done:;
+}
+
+struct triples {
+ int max_c;
+ int a, b, c;
+ int cco_state;
+};
+
+int triples_coro(struct triples* t) {
+ cco_routine(t) {
+ for (t->c = 5;; ++t->c) {
+ for (t->a = 1; t->a < t->c; ++t->a) {
+ for (t->b = t->a + 1; t->b < t->c; ++t->b) {
+ if ((int64_t)t->a * t->a +
+ (int64_t)t->b * t->b ==
+ (int64_t)t->c * t->c)
+ {
+ if (t->c > t->max_c)
+ cco_return;
+ cco_yield();
+ }
+ }
+ }
+ }
+ cco_final:
+ puts("done");
+ }
+ return 0;
+}
+
+int gcd(int a, int b) {
+ while (b) {
+ int t = a % b;
+ a = b;
+ b = t;
+ }
+ return a;
+}
+
+int main(void)
+{
+ puts("Vanilla triples:");
+ triples_vanilla(20);
+
+ puts("\nCoroutine triples with GCD = 1:");
+ struct triples t = {.max_c = 100};
+ int n = 0;
+
+ cco_blocking_call(triples_coro(&t)) {
+ if (gcd(t.a, t.b) > 1)
+ continue;
+ if (++n <= 20)
+ printf("%d: {%d, %d, %d}\n", n, t.a, t.b, t.c);
+ else
+ cco_stop(&t);
+ }
+}