1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
As illustrated, there are various ways to obtain an Awaiter from a tt(co_await
expr) statement. The shortest route goes like this:
itemization(
it() According to figure ref(CoAwaitFig) tt(expr) is an awaitable if the
em(current) coroutine's tt(State) class does em(not) have an
tt(await_transform) member. In that case tt(expr) is the awaitable;
it() According to figure ref(AwaitableFig) if tt(expr's) type does em(not)
have an tt(operator co_await) member then tt(expr's) type is the
tt(Awaiter).
)
So, the nested tt(Start::State) class only has to provide the standard
members of the coroutine handler's tt(State) class. As those are all provided
by the generic tt(PromiseBase) class (section ref(STATEBASE)) tt(State) needs
no additional members:
verb( // nested under the Start handler class:
struct State: public PromiseBase<Start, State>
{};)
Similar considerations apply to the other three handler classes: their
tt(State) classes are also derived from tt(PromiseBase<Handler,
State>). However, as the tt(coDone) coroutine also uses tt(co_return), the
tt(Done::State) state class must have its own a tt(return_void) member:
verb( // nested under the Done handler class:
struct State: public PromiseBase<Done, State>
{
void return_void() const;
};
// implementation in done.h:
inline void Done::State::return_void() const
{})
As our FSA allows transitions from tt(Digit) and tt(Letter) back to
tt(Start) the tt(Start) handler class itself is an Awaiter (as are tt(Digit,
Letter,) and tt(Done)). Section ref(AWAITER) described the requirements and
basic definition of Awaiter classes.
From the point of view of FSAs the most interesting part is how to switch from
one coroutine to another. As illustrated in figure ref(AwaiterFig) this
requires a member tt(await_suspend) which em(receives) the handle of the
coroutine using the tt(co_await) statement, and returns em(some coroutine's)
handle. So:
itemization(
itt(co_await expr) is used, where tt(expr) is a coroutine's handler, which
is also an Awaiter;
it() the current coroutine passes its own handle to tt(expr's
await_suspend) member;
it() the handle returned by tt(expr's await_suspend) member determines
which coroutine is resumed, suspending the coroutine using
tt(co_await);
itt(expr) returns its own handle, thereby realizing the FSA's state switch
from the current coroutine to tt(expr's) coroutine.
)
Here is the interface of tt(coStart's) handler class as well as the definition
of its tt(await_suspend) member. Since the tt(coStart) coroutine may be
resumed by several other coroutines it is unknown which coroutine's handle was
passed to tt(Start::await_suspend), and so tt(await_suspend) is a member
template, which simply returns tt(Start's) handle.
verbinsert(-s4 //start demo/fsa/start/start.h)
As the member tt(Start's wait_suspend) returns tt(Start's d_handle), the
coroutine containing the tt(co_await g_start) statement is suspended, and the
tt(co_start) coroutine is resumed (see also figure ref(AwaiterFig)).
The implementations of the tt(Start) handler's constructor and destructor are
straightforward: the constructor stores the coroutine's handle in its
tt(d_handle) data member, the destructor uses the (language provided) member
tt(destroy) to properly end the tt(Start::State's) coroutine handle. Here are
their implementations:
verbinsert(-s4 //impl demo/fsa/start/start1.cc)
verbinsert(-s4 //impl demo/fsa/start/destructor.cc)
|