Skip to content

Latest commit

 

History

History
118 lines (96 loc) · 2.68 KB

InteractingWithStacklessCoroutine.md

File metadata and controls

118 lines (96 loc) · 2.68 KB

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:

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:

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:

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

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:

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

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:

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:

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

If it need an executor:

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.

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