Skip to content

Commit

Permalink
Update to coroutine.h
Browse files Browse the repository at this point in the history
  • Loading branch information
tylov committed May 16, 2024
1 parent 92065c5 commit 0e05547
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 36 deletions.
6 changes: 3 additions & 3 deletions docs/coroutine_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ NB! ***cco_yield\*()*** / ***cco_await\*()*** may not be called from within a `s
| | `cco_await_call(cocall, retbit);` | Await for subcoro's return to be in (retbit \| CCO_DONE) |
| | `cco_return;` | Return from coroutine (inside cco_routine) |
| | ***c_filter() interoperability with coroutine iterators***: ||
| | `cco_take(num);` | Use instead of *c_flt_take(num)* to ensure cleanup state |
| | `cco_takewhile(predicate);` | Use instead of *c_flt_takewhile(pred)* to ensure cleanup state |
| | `cco_takewhile(predicate);` | Use instead of *c_flt_takewhile(pred)* to ensure cleanup state |
| | `cco_flt_take(num);` | Use instead of *c_flt_take(num)* to ensure cleanup state |
| | `cco_flt_takewhile(predicate);` | Use instead of *c_flt_takewhile(pred)* to ensure cleanup state |
| | `cco_flt_takewhile(predicate);` | Use instead of *c_flt_takewhile(pred)* to ensure cleanup state |
| | ***Container iteration in coroutines***: ||
| | `c_foreach_iter(external_it, ctype, cnt)` | Use iterator stored in coroutine object |
| | `c_foreach_reverse_iter(external_it, ctype, cnt)` | Iterate in reverse order |
Expand Down
58 changes: 39 additions & 19 deletions include/stc/coroutine.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ int main(void) {
#include "common.h"

enum {
CCO_STATE_INIT = 0,
CCO_STATE_FINAL = -1,
CCO_STATE_DONE = -2,
};
Expand All @@ -72,19 +73,9 @@ typedef enum {
#define cco_suspended(co) ((co)->cco_state > 0)
#define cco_done(co) ((co)->cco_state == CCO_STATE_DONE)

// Use with c_filter:
#define cco_take(n) (c_flt_take(n), _fl.done ? _it.cco_state = CCO_STATE_FINAL : 1)
#define cco_takewhile(pred) (c_flt_takewhile(pred), _fl.done ? _it.cco_state = CCO_STATE_FINAL : 1)

#define c_foreach_iter(existing_it, C, cnt) \
for (existing_it = C##_begin(&cnt); (existing_it).ref; C##_next(&existing_it))
#define c_foreach_reverse_iter(existing_it, C, cnt) \
for (existing_it = C##_rbegin(&cnt); (existing_it).ref; C##_rnext(&existing_it))


#define cco_routine(co) \
for (int* _state = &(co)->cco_state; *_state != CCO_STATE_DONE; *_state = CCO_STATE_DONE) \
_resume: switch (*_state) case 0: // thanks, @liigo!
_resume: switch (*_state) case CCO_STATE_INIT: // thanks, @liigo!

#define cco_yield cco_yield_v(CCO_YIELD)
#define cco_yield_v(ret) \
Expand Down Expand Up @@ -142,11 +133,20 @@ typedef enum {
return value; \
} while (0)


/* ============ ADVANCED, OPTIONAL ============= */

/*
* Iterator (for generators)
* User define: Gen must be an existing typedef struct:
* Gen_iter Gen_begin(Gen* g); // function
* int Gen_next(Gen_iter* it); // coroutine
* Iterators (for generators)
* Gen must be an existing typedef struct, i.e., these must be defined:
* Gen_iter Gen_begin(Gen* g); // return a coroutine object, advanced to the first yield
* int Gen_next(Gen_iter* it); // resume the coroutine
*
* Gen_iter Gen_begin(Gen* g) { // basic implementation
* Gen_iter it = {.ref=g};
* Gen_next(&it);
* return it;
* }
*/

#define cco_iter_struct(Gen, ...) \
Expand All @@ -157,6 +157,7 @@ typedef enum {
__VA_ARGS__ \
} Gen##_iter


/*
* Tasks
*/
Expand All @@ -170,14 +171,14 @@ struct cco_runtime;
__VA_ARGS__ \
}

typedef cco_task_struct(cco_task, /**/) cco_task;
cco_task_struct(cco_task, /**/); /* Define base Task struct type */

typedef struct cco_runtime {
int result, top; cco_task* stack[];
int result, top; struct cco_task* stack[];
} cco_runtime;

#define cco_cast_task(task) \
((cco_task *)(task) + 0*sizeof((task)->cco_func(task, (cco_runtime*)0) + ((int*)0 == &(task)->cco_state)))
((struct cco_task *)(task) + 0*sizeof((task)->cco_func(task, (cco_runtime*)0) + ((int*)0 == &(task)->cco_state)))

#define cco_resume_task(task, rt) \
(task)->cco_func(task, rt)
Expand All @@ -193,10 +194,28 @@ typedef struct cco_runtime {
#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)}}; \
for (struct { int result, top; struct cco_task* stack[STACKDEPTH]; } rt = {.stack={cco_cast_task(task)}}; \
(((rt.result = cco_resume_task(rt.stack[rt.top], (cco_runtime*)&rt)) & \
~rt.stack[rt.top]->cco_expect) || --rt.top >= 0); )


/*
* Use with c_filter:
*/

#define cco_flt_take(n) (c_flt_take(n), _fl.done ? _it.cco_state = CCO_STATE_FINAL : 1)
#define cco_flt_takewhile(pred) (c_flt_takewhile(pred), _fl.done ? _it.cco_state = CCO_STATE_FINAL : 1)

/*
* Iterate containers with already defined iterator (prefer to use in coroutines only):
*/

#define c_foreach_iter(existing_it, C, cnt) \
for (existing_it = C##_begin(&cnt); (existing_it).ref; C##_next(&existing_it))
#define c_foreach_reverse_iter(existing_it, C, cnt) \
for (existing_it = C##_rbegin(&cnt); (existing_it).ref; C##_rnext(&existing_it))


/*
* Semaphore
*/
Expand All @@ -215,6 +234,7 @@ typedef struct { intptr_t count; } cco_sem;
#define cco_sem_from(value) ((cco_sem){value})
#define cco_sem_set(sem, value) ((sem)->count = value)


/*
* Timer
*/
Expand Down
4 changes: 2 additions & 2 deletions misc/examples/coroutines/generator.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ int main(void)
puts("\nGet the 10 first triples with c < 40, using c_filter:");
c_filter(Triple, triple
, (value->c < 40)
&& (cco_take(10), // NB! use cco_take(n) instead of c_flt_take(n)
// to ensure coroutine/iter cleanup if needed
&& (cco_flt_take(10), // NB! use cco_flt_take(n) instead of c_flt_take(n)
// to ensure coroutine/iter cleanup if needed
printf("%d: (%d, %d, %d)\n", c_flt_getcount(), value->a, value->b, value->c))
);
}
24 changes: 12 additions & 12 deletions misc/examples/coroutines/scheduler.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@
#include <stdio.h>
#include "stc/coroutine.h"

#define i_TYPE Tasks, cco_task*
#define i_TYPE Tasks, struct cco_task*
#define i_keydrop(x) { puts("free task"); free(*x); }
#define i_no_clone
#include "stc/queue.h"

typedef struct {
Tasks tasks;
} cco_scheduler;
} Scheduler;

void cco_scheduler_drop(cco_scheduler* sched) {
void Scheduler_drop(Scheduler* sched) {
Tasks_drop(&sched->tasks);
}

int cco_scheduler_run(cco_scheduler* sched) {
int Scheduler_run(Scheduler* sched) {
while (!Tasks_is_empty(&sched->tasks)) {
cco_task* task = Tasks_pull(&sched->tasks);
struct cco_task* task = Tasks_pull(&sched->tasks);
if (cco_resume_task(task, NULL))
Tasks_push(&sched->tasks, task);
else
Expand All @@ -26,7 +26,7 @@ int cco_scheduler_run(cco_scheduler* sched) {
return 0;
}

static int taskA(cco_task* task, cco_runtime* rt) {
static int taskA(struct cco_task* task, cco_runtime* rt) {
(void)rt;
cco_routine(task) {
puts("Hello, from task A");
Expand All @@ -40,7 +40,7 @@ static int taskA(cco_task* task, cco_runtime* rt) {
return 0;
}

static int taskB(cco_task* task, cco_runtime* rt) {
static int taskB(struct cco_task* task, cco_runtime* rt) {
(void)rt;
cco_routine(task) {
puts("Hello, from task B");
Expand All @@ -53,13 +53,13 @@ static int taskB(cco_task* task, cco_runtime* rt) {
}

void Use(void) {
cco_scheduler sched = {.tasks = c_init(Tasks, {
c_new(cco_task, {.cco_func=taskA}),
c_new(cco_task, {.cco_func=taskB}),
Scheduler sched = {.tasks = c_init(Tasks, {
c_new(struct cco_task, {.cco_func=taskA}),
c_new(struct cco_task, {.cco_func=taskB}),
})};

cco_scheduler_run(&sched);
cco_scheduler_drop(&sched);
Scheduler_run(&sched);
Scheduler_drop(&sched);
}

int main(void)
Expand Down

0 comments on commit 0e05547

Please sign in to comment.