coroutine
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.
API
-
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.
C99 example
#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) ... }
C++11 example
#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) ... } };