Skip to content

Commit

Permalink
coroutine: Simplified task running: using a "linked list" instead of …
Browse files Browse the repository at this point in the history
…a "stack" to manage control-flow.
  • Loading branch information
Tyge Lovset committed Nov 13, 2024
1 parent 8021a89 commit 7f06da4
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 42 deletions.
63 changes: 27 additions & 36 deletions include/stc/coroutine.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,20 +162,21 @@ typedef struct {


/*
* Tasks
* Tasks: typesafe coroutine function objects
*/

struct cco_task;
typedef struct {
int32_t result; int16_t top, maxdepth;
struct cco_task* stack[];
int result;
struct cco_task* curr;
} cco_runtime;

#define cco_task_struct(Task) \
struct Task; \
typedef struct { \
int (*func)(struct Task*, cco_runtime*); \
int state, await; \
struct cco_task* prev; \
} Task##_state; \
struct Task

Expand All @@ -190,67 +191,57 @@ typedef struct cco_task cco_task;

#define cco_cancel_task(task, rt) \
do { \
cco_task* _t = cco_cast_task(task); \
cco_stop(_t); _t->cco.func(_t, rt); \
cco_task* _task = cco_cast_task(task); \
cco_stop(_task); _task->cco.func(_task, rt); \
} while (0)

#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) cco_await_task_v(task, rt, awaitbits, CCO_AWAIT)
#define cco_await_task_v(task, rt, awaitbits, suspendval) \
do { \
((rt)->stack[++(rt)->top] = cco_cast_task(task))->cco.await = (awaitbits); \
do {{cco_task* _prev = (rt)->curr; \
(rt)->curr = cco_cast_task(task); \
(rt)->curr->cco.await = (awaitbits); \
(rt)->curr->cco.prev = _prev;} \
cco_yield_v(suspendval); \
} while (0)

#define cco_yield_task(task, rt) cco_yield_task_v(task, rt, CCO_AWAIT)
#define cco_yield_task_v(task, rt, suspendval) \
do { \
{ cco_task* _t = cco_cast_task(task); \
_t->cco.await = (rt)->stack[(rt)->top]->cco.await; \
(rt)->stack[(rt)->top] = _t; } \
do {{cco_task* _task = cco_cast_task(task); \
_task->cco.await = (rt)->curr->cco.await; \
_task->cco.prev = (rt)->curr->cco.prev; \
(rt)->curr = _task;} \
cco_yield_v(suspendval); \
} while (0)

#define cco_run_task(...) c_MACRO_OVERLOAD(cco_run_task, __VA_ARGS__)
#define cco_run_task_1(task) cco_run_task_3(task, _rt, 16)
#define cco_run_task_3(task, rt, STACKDEPTH) \
for (struct {int result, top; struct cco_task *stack[STACKDEPTH], *curr;} \
rt = {.stack = {cco_cast_task(task)}} \
; (rt.curr = rt.stack[rt.top], \
((rt.result = cco_resume_task(rt.curr, (cco_runtime*)&rt)) & ~rt.curr->cco.await) \
|| --rt.top >= 0) \
#define cco_run_task_1(task) cco_run_task_2(task, _rt)
#define cco_run_task_2(task, rt) \
for (struct {int result; cco_task *curr;} \
rt = {.curr = cco_cast_task(task)} \
; rt.result = cco_resume_task(rt.curr, (cco_runtime*)&rt), \
(rt.result & ~rt.curr->cco.await) || (rt.curr = rt.curr->cco.prev) != NULL \
; )

struct cco_taskrunner {
cco_runtime* rt;
cco_runtime rt;
cco_state cco;
};

#define cco_make_taskrunner(task, maxdepth) _cco_make_taskrunner(cco_cast_task(task), maxdepth)
static inline
struct cco_taskrunner _cco_make_taskrunner(cco_task* task, int depth) {
struct cco_taskrunner ex = {
.rt = (cco_runtime *)malloc(sizeof(cco_runtime) + depth*sizeof(cco_task*)),
};
ex.rt->maxdepth = depth;
ex.rt->stack[0] = task;
return ex;
}
#define cco_make_taskrunner(task) \
((struct cco_taskrunner){.rt = {.curr = cco_cast_task(task)}})

static inline int cco_taskrunner(struct cco_taskrunner* co) {
cco_routine (co) {
while (1) {{
cco_task* curr = co->rt->stack[co->rt->top];
co->rt->result = cco_resume_task(curr, co->rt);
if (!((co->rt->result & ~curr->cco.await) || --co->rt->top >= 0)) {
cco_task* curr = co->rt.curr;
co->rt.result = cco_resume_task(curr, &co->rt);
if (!((co->rt.result & ~curr->cco.await) || (co->rt.curr = curr->cco.prev) != NULL)) {
break;
}}
cco_yield;
}
cco_cleanup:
free(co->rt);
co->rt = NULL;
}
return 0;
}
Expand Down Expand Up @@ -302,7 +293,7 @@ static inline int cco_taskrunner(struct cco_taskrunner* co) {
#define cco_foreach_reverse(existing_it, C, cnt) \
for (existing_it = C##_rbegin(&cnt); (existing_it).ref; C##_rnext(&existing_it))

/*
/*
* Using c_filter with coroutine iterators:
*/
#define cco_flt_take(n) \
Expand Down
12 changes: 7 additions & 5 deletions misc/examples/coroutines/cotasks2.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

// PRODUCER
cco_task_struct (produce_items) {
produce_items_state cco; // must be first
produce_items_state cco; // must be first (compile-time checked)
struct consume_items* consumer;
Inventory inv;
int limit, batch, serial, total;
Expand Down Expand Up @@ -86,8 +86,10 @@ int main(void)
.producer = &producer,
};
producer.consumer = &consumer;
//cco_run_task(&producer);

struct cco_taskrunner ex = cco_make_taskrunner(&producer, 2);
cco_run_coroutine(cco_taskrunner(&ex));
#if 1
cco_run_task(&producer);
#else
struct cco_taskrunner runner = cco_make_taskrunner(&producer);
cco_run_coroutine(cco_taskrunner(&runner));
#endif
}
6 changes: 5 additions & 1 deletion misc/examples/coroutines/filetask.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,12 @@ int main(void)

// Execute coroutine as top-level blocking
int loop = 0;
#if 0
cco_run_task(&countTask) { ++loop; }

#else
struct cco_taskrunner runner = cco_make_taskrunner(&countTask);
cco_run_coroutine(cco_taskrunner(&runner)) { ++loop; }
#endif
printf("line count = %d\n", countTask.lineCount);
printf("exec count = %d\n", loop);
}

0 comments on commit 7f06da4

Please sign in to comment.