summaryrefslogtreecommitdiffhomepage
path: root/misc/examples/coroutines
diff options
context:
space:
mode:
authortylov <[email protected]>2023-07-20 15:09:10 +0200
committertylov <[email protected]>2023-07-20 15:12:29 +0200
commit900295256d825fc323149cd223c49787f32a3696 (patch)
tree6c79cf4209e3975bb6865e2940b9cb56ea469c73 /misc/examples/coroutines
parent224a04f7fa7549ed94d2a1415eb25829e39a7cca (diff)
downloadSTC-modified-900295256d825fc323149cd223c49787f32a3696.tar.gz
STC-modified-900295256d825fc323149cd223c49787f32a3696.zip
Moved examples to sub-directories. Added cotask1.c cotask2.c examples.
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.c103
-rw-r--r--misc/examples/coroutines/dining_philosophers.c105
-rw-r--r--misc/examples/coroutines/generator.c55
-rw-r--r--misc/examples/coroutines/scheduler.c74
-rw-r--r--misc/examples/coroutines/triples.c72
9 files changed, 724 insertions, 0 deletions
diff --git a/misc/examples/coroutines/cointerleave.c b/misc/examples/coroutines/cointerleave.c
new file mode 100644
index 00000000..599ceaab
--- /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/calgo.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_block_on(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..a13f6be5
--- /dev/null
+++ b/misc/examples/coroutines/coread.c
@@ -0,0 +1,41 @@
+#define i_implement
+#include <stc/cstr.h>
+#include <stc/algo/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_cleanup:
+ 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_block_on(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..b8dfaa13
--- /dev/null
+++ b/misc/examples/coroutines/coroutines.c
@@ -0,0 +1,112 @@
+#include <stc/algo/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_cleanup:
+ 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_cleanup:
+ 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_on(prime(&g->prm));
+ cco_await_on(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_on(prime(&g->prm));
+
+ cco_cleanup:
+ puts("final combined");
+ }
+ return 0;
+}
+
+int main(void)
+{
+ struct combined c = {.prm={.count=8}, .fib={14}};
+ int res;
+
+ cco_block_on(combined(&c), &res) {
+ 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..c87582f1
--- /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/algo/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_timer_await(&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 str;
+ int cco_state;
+};
+
+int produce_items(struct produce_items* p)
+{
+ cco_routine (p) {
+ p->str = cstr_null;
+ while (true)
+ {
+ cco_await(next_value(&p->next) != CCO_AWAIT);
+ cstr_printf(&p->str, "item %d", p->next.val);
+ print_time();
+ printf("produced %s\n", cstr_str(&p->str));
+ cco_yield();
+ }
+ cco_cleanup:
+ cstr_drop(&p->str);
+ 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->str));
+ }
+ cco_cleanup:
+ puts("done consume");
+ }
+ return 0;
+}
+
+int main(void)
+{
+ struct produce_items produce = {0};
+ struct consume_items consume = {.n=5};
+ int count = 0;
+
+ cco_block_on(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..293583bc
--- /dev/null
+++ b/misc/examples/coroutines/cotasks2.c
@@ -0,0 +1,103 @@
+// 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/algo/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_timer_await(&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 str;
+);
+
+int produce_items(struct produce_items* p, cco_runtime* rt)
+{
+ cco_routine (p) {
+ p->str = cstr_null;
+ while (true)
+ {
+ // await for next CCO_YIELD in next_value()
+ 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));
+ cco_yield();
+ }
+ cco_cleanup:
+ cstr_drop(&p->str);
+ 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) {
+ 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.str));
+ }
+ cco_cleanup:
+ cco_stop(&c->produce);
+ cco_resume(&c->produce, rt);
+ puts("done consume");
+ }
+ return 0;
+}
+
+int main(void)
+{
+ struct consume_items consume = {
+ .n=5,
+ .cco_fn=consume_items,
+ .produce={.cco_fn=produce_items, .next={.cco_fn=next_value}},
+ };
+ int count = 0;
+
+ cco_block_task(&consume)
+ {
+ ++count;
+ //cco_sleep(0.001);
+ //if (consume.i == 3)
+ // cco_stop(&consume);
+ }
+ printf("count: %d\n", count);
+} \ No newline at end of file
diff --git a/misc/examples/coroutines/dining_philosophers.c b/misc/examples/coroutines/dining_philosophers.c
new file mode 100644
index 00000000..a5063a42
--- /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/algo/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_timer_await(&p->tm, duration);
+
+ printf("Philosopher %d is hungry...\n", p->id);
+ cco_sem_await(p->left_fork);
+ cco_sem_await(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_sem_release(p->left_fork);
+ cco_sem_release(p->right_fork);
+ }
+
+ cco_cleanup:
+ 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_cleanup:
+ 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));
+
+ while (!cco_done(&dine)) {
+ if (cco_timer_expired(&tm))
+ cco_stop(&dine);
+ dining(&dine); // resume
+ cco_sleep(0.001);
+ ++n;
+ }
+ printf("n=%d\n", n);
+}
diff --git a/misc/examples/coroutines/generator.c b/misc/examples/coroutines/generator.c
new file mode 100644
index 00000000..a15f9ba5
--- /dev/null
+++ b/misc/examples/coroutines/generator.c
@@ -0,0 +1,55 @@
+// https://quuxplusone.github.io/blog/2019/03/06/pythagorean-triples/
+
+#include <stc/algo/coroutine.h>
+#include <stdio.h>
+
+typedef struct {
+ int size;
+ int a, b, c;
+} Triple, Triple_value;
+
+typedef struct {
+ Triple_value* ref;
+ int count;
+ int cco_state;
+} Triple_iter;
+
+int Triple_next(Triple_iter* it) {
+ Triple_value* g = it->ref;
+ cco_routine(it)
+ {
+ for (g->c = 5; g->size; ++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->size)
+ cco_return;
+ cco_yield();
+ }
+ }
+ }
+ }
+ cco_cleanup:
+ it->ref = NULL;
+ }
+ return 0;
+}
+
+Triple_iter Triple_begin(Triple* g) {
+ Triple_iter it = {.ref=g};
+ Triple_next(&it);
+ return it;
+}
+
+
+int main(void)
+{
+ puts("Pythagorean triples with c < 100:");
+ Triple triple = {.size=30}; // max number of triples
+ c_foreach (i, Triple, triple) {
+ if (i.ref->c < 100)
+ printf("%u: (%d, %d, %d)\n", i.count, i.ref->a, i.ref->b, i.ref->c);
+ else
+ cco_stop(&i);
+ }
+}
diff --git a/misc/examples/coroutines/scheduler.c b/misc/examples/coroutines/scheduler.c
new file mode 100644
index 00000000..38defd0f
--- /dev/null
+++ b/misc/examples/coroutines/scheduler.c
@@ -0,0 +1,74 @@
+// https://www.youtube.com/watch?v=8sEe-4tig_A
+#include <stdio.h>
+#include <stc/calgo.h>
+
+struct Task {
+ int (*fn)(struct Task*);
+ int cco_state;
+ struct Scheduler* sched;
+};
+
+#define i_type Scheduler
+#define i_key struct Task
+#include <stc/cqueue.h>
+
+static bool schedule(Scheduler* sched)
+{
+ struct Task task = *Scheduler_front(sched);
+ Scheduler_pop(sched);
+
+ if (!cco_done(&task))
+ task.fn(&task);
+
+ return !Scheduler_empty(sched);
+}
+
+static int push_task(const struct Task* task)
+{
+ Scheduler_push(task->sched, *task);
+ return CCO_YIELD;
+}
+
+
+static int taskA(struct Task* task)
+{
+ cco_routine(task) {
+ puts("Hello, from task A");
+ cco_yield_v(push_task(task));
+ puts("A is back doing work");
+ cco_yield_v(push_task(task));
+ puts("A is back doing more work");
+ cco_yield_v(push_task(task));
+ puts("A is back doing even more work");
+ }
+ return 0;
+}
+
+static int taskB(struct Task* task)
+{
+ cco_routine(task) {
+ puts("Hello, from task B");
+ cco_yield_v(push_task(task));
+ puts("B is back doing work");
+ cco_yield_v(push_task(task));
+ puts("B is back doing more work");
+ }
+ return 0;
+}
+
+void Use(void)
+{
+ Scheduler scheduler = c_init(Scheduler, {
+ {.fn=taskA, .sched=&scheduler},
+ {.fn=taskB, .sched=&scheduler},
+ });
+
+ while (schedule(&scheduler)) {}
+
+ Scheduler_drop(&scheduler);
+}
+
+int main(void)
+{
+ Use();
+}
diff --git a/misc/examples/coroutines/triples.c b/misc/examples/coroutines/triples.c
new file mode 100644
index 00000000..9f2fcc1e
--- /dev/null
+++ b/misc/examples/coroutines/triples.c
@@ -0,0 +1,72 @@
+// https://quuxplusone.github.io/blog/2019/03/06/pythagorean-triples/
+
+#include <stc/algo/coroutine.h>
+#include <stdio.h>
+
+int gcd(int a, int b) {
+ while (b) {
+ int t = a % b;
+ a = b;
+ b = t;
+ }
+ return a;
+}
+
+void triples_vanilla(int n) {
+ for (int c = 5, i = 0; n; ++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 && gcd(a, b) == 1) {
+ printf("%d: {%d, %d, %d}\n", ++i, a, b, c);
+ if (--n == 0) goto done;
+ }
+ }
+ }
+ }
+ done:;
+}
+
+struct triples {
+ int size, count;
+ int a, b, c;
+ int cco_state;
+};
+
+int triples_coro(struct triples* t) {
+ cco_routine(t) {
+ t->count = 0;
+ for (t->c = 5; t->size; ++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->count++ == t->size)
+ cco_return;
+ cco_yield();
+ }
+ }
+ }
+ }
+ cco_cleanup:
+ puts("done");
+ }
+ return 0;
+}
+
+int main(void)
+{
+ puts("Vanilla triples:");
+ triples_vanilla(5);
+
+ puts("\nCoroutine triples:");
+ struct triples t = {.size=INT32_MAX};
+ int n = 0;
+
+ while (triples_coro(&t)) {
+ if (gcd(t.a, t.b) > 1)
+ continue;
+ if (t.c < 100)
+ printf("%d: {%d, %d, %d}\n", ++n, t.a, t.b, t.c);
+ else
+ cco_stop(&t);
+ }
+}