This is a header-only stackless coroutine implementation in standard C99 and C++11. coroutine.h contains a C adaptation of the C++ stackless coroutine from Boost.Asio by Christopher M. Kohlhoff. This is itself a variant of Simon Tatham's Coroutines in C, which was inspired by Duff's device. The API is designed to be a more powerfull version of Protothreads with a more natural syntax.
The implementation (ab)uses the switch statement. It is therefore not possible to yield from a coroutine from within a nested switch statement.
Since the implementation is stackless, variables local to the coroutine are not stored between invocations. In C++, this drawback can be partially mitigated by implementing the coroutine as a function object (see coroutine.hpp) and making all local variables (private) data members.
-
co_reenter(ctx)
: Defines the body of a stackless coroutine. When the body is executed at runtime, control jumps to the ___location immediately following the lastco_yield
orco_fork
statement.Note that a function MUST NOT contain multiple
co_reenter
expressions with the same context. -
co_yield <expression>
: Stores the context of the current stackless coroutine, evaluates the expression following theco_yield
keyword, if any, and exits the scope of the #co_reenter statement.co_yield break
terminates the coroutine.co_yield continue
is equivalent toco_yield
.A
co_yield
expression is valid only within aco_reenter
statement. Sinceco_reenter
is implemented using aswitch
statement,co_yield
CANNOT be used from within a nestedswitch
statement. -
co_fork <expression>
: "Forks" a coroutine and executes the expression following theco_fork
keyword as a child. This expression will typically create a copy of the coroutine context. After the expression completes, the coroutine continues and as a parent. If the coroutine is reentered with (the copy of) the context created byco_fork
, the coroutine continues as a child until the nextco_yield
statement.See coroutine.h for an example of
co_fork
.
#include "coroutine.h"
void my_coroutine(co_ctx_t* ctx) {
...
// statements executed on every invocation of my_coroutine()
...
co_reenter (ctx) {
assert(!co_is_ready(ctx));
...
// statements executed on the first invocation of
// my_coroutine()
...
// Store the context and exit the scope of the co_reenter
// statement.
co_yield;
...
// statements executed on the second invocation of
// my_coroutine()
...
co_yield;
...
// statements executed on the third invocation of
// my_coroutine()
...
}
...
// statements executed on every invocation of my_coroutine() (unless the
// function returns early)
...
}
#include "coroutine.hpp"
class MyCoroutine : public Coroutine {
public:
void operator()() {
...
// statements executed on every invocation
...
co_reenter (this) {
...
// statements executed on the first invocation
...
// Store the context and exit the scope of the co_reenter statement.
co_yield;
...
// statements executed on the second invocation
...
co_yield;
...
// statements executed on the third invocation
...
}
...
// statements executed on every invocation (unless this function returns
// early)
...
}
};