Skip to content

We need to offer multiple interfaces for an method implemented in stackless coroutine to make it could be used by different users. For example, we implement a Read method:

cpp
Lazy<size_t> Read();

But the caller of Read might be stackless coroutine, stackful coroutine and normal function. Note that the stackless coroutine would be polluting generally. So how should we call it in above cases?

Stackless coroutine ​

When the caller is also a stackless coroutine, we could co_await it directly:

cpp
Lazy<size_t> foo() {
    auto val = co_await Read();
    // ...
}

Stackful coroutine ​

When the caller is in stackful coroutine, we could make a wrapper for Read by await method:

cpp
Lazy<size_t> Read();
Future<size_t> ReadAsync() {
    return await(nullptr, Read);
}
void foo() { // in Uthread
    auto val = ReadAsync().get();
    // ...
}

await ​

await method is used for stackful coroutine to wait for stackless coroutine. await has two overloads:

Class member method ​

cpp
template <class B, class Fn, class C, class... Ts>
decltype(auto) await(Executor* ex, Fn B::* fn, C* cls, Ts&&... ts);

Given the return type of Fn is Lazy<T>, the return type of await would be T.

We could use it as:

cpp
class Foo {
public:
   lazy<T> bar(int a, int b) {}
};
Foo f;
await(ex, &Foo::bar, &f, a, b);

ex is the executor we specified. It could be null if we don't need an executor.

Normal Method ​

cpp
template <class Fn, class... Ts>
decltype(auto) await(Executor* ex, Fn&& fn, Ts&&... ts);

Given the return type of Fn is Lazy<T>, the return type of await would be T.

We could use it as:

cpp
lazy<T> foo(Ts&&...);
await(ex, foo, Ts&&...);
auto lambda = [](Ts&&...) -> lazy<T> {
    // ...
};
await(ex, lambda, Ts&&...);

ex is the executor we specified. It could be null if we don't need an executor.

Normal function ​

The normal function could make a wrapper for Read by syncAwait method:

cpp
Lazy<size_t> Read();
size_t ReadSync() {
    return syncAwait(Read());
}
void foo() {
    auto val = ReadSync();
    // ...
}

If it need an executor:

cpp
Lazy<size_t> Read();
size_t ReadSync(Executor* ex) {
    return syncAwait(Read().via(ex));
}
void foo() {
    auto *ex = getExecutor();
    auto val = ReadSync(ex);
    // ...
}

Non-blocking call ​

No matter the caller is a normal function, a stackful coroutine or a stackless coroutine, the caller could call a stackless coroutine in a non-blocking way by .start method.

cpp
Lazy<size_t> Read();
void ReadCallback(std::function<void(Try<size_t>)> callback) {
    Read().start(callback);
}
void foo() {
    ReadCallback(callback);
    // continue to execute immediately.
}

This website is released under the MIT License.