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_yieldorco_forkstatement.Note that a function MUST NOT contain multiple
co_reenterexpressions with the same context. -
co_yield <expression>: Stores the context of the current stackless coroutine, evaluates the expression following theco_yieldkeyword, if any, and exits the scope of the #co_reenter statement.co_yield breakterminates the coroutine.co_yield continueis equivalent toco_yield.A
co_yieldexpression is valid only within aco_reenterstatement. Sinceco_reenteris implemented using aswitchstatement,co_yieldCANNOT be used from within a nestedswitchstatement. -
co_fork <expression>: "Forks" a coroutine and executes the expression following theco_forkkeyword 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_yieldstatement.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)
...
}
};