blob: 87abb2e4f99842368be9059d4db626867dc79947 (
plain) (
blame)
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once
#include "waiting_for.h"
#include <coroutine>
#include <optional>
#include <exception>
#include <utility>
namespace vespalib::coro {
/**
* coroutine return type
*
* The coroutine is lazy (will suspend in initial_suspend) and
* destroyed from the outside (will suspend in final_suspend). Waiting
* for a Lazy<T> using co_await will use symmetric transfer to suspend
* the waiting coroutine and resume this one. The waiting coroutine
* is registered as a continuation and will be resumed again once the
* result is available (also using symmetric transfer). The result is
* assumed to be produced asynchronously. If you need to access it
* from the outside (in that specific thread); use sync_wait.
**/
template <std::movable T>
class [[nodiscard]] Lazy {
public:
struct promise_type final : PromiseState<T> {
using PromiseState<T>::result;
Lazy<T> get_return_object() { return Lazy(Handle::from_promise(*this)); }
static std::suspend_always initial_suspend() noexcept { return {}; }
static auto final_suspend() noexcept {
struct awaiter {
bool await_ready() const noexcept { return false; }
std::coroutine_handle<> await_suspend(Handle handle) const noexcept {
return handle.promise().waiter;
}
void await_resume() const noexcept {}
};
return awaiter();
}
template <typename RET>
void return_value(RET &&ret_value) {
result.set_value(std::forward<RET>(ret_value));
}
void unhandled_exception() noexcept {
result.set_error(std::current_exception());
}
promise_type() noexcept : PromiseState<T>() {}
~promise_type();
};
using Handle = std::coroutine_handle<promise_type>;
private:
Handle _handle;
template <typename RET>
struct WaitFor {
Handle handle;
WaitFor(Handle handle_in) noexcept : handle(handle_in) {}
bool await_ready() const noexcept { return handle.done(); }
Handle await_suspend(std::coroutine_handle<> waiter) const noexcept {
handle.promise().waiter = waiter;
return handle;
}
decltype(auto) await_resume() const { return RET::get(handle.promise()); }
};
struct LValue {
static T& get(auto &&promise) { return promise.result.get_value(); }
};
struct RValue {
static T&& get(auto &&promise) { return std::move(promise.result).get_value(); }
};
struct Result {
static Received<T>&& get(auto &&promise) { return std::move(promise.result); }
};
struct Nothing {
static void get(auto &&) {}
};
public:
Lazy(const Lazy &) = delete;
Lazy &operator=(const Lazy &) = delete;
explicit Lazy(Handle handle_in) noexcept : _handle(handle_in) {}
Lazy(Lazy &&rhs) noexcept : _handle(std::exchange(rhs._handle, nullptr)) {}
auto operator co_await() & noexcept { return WaitFor<LValue>(_handle); }
auto operator co_await() && noexcept { return WaitFor<RValue>(_handle); }
auto forward() noexcept { return WaitFor<Result>(_handle); }
auto done() noexcept { return WaitFor<Nothing>(_handle); }
~Lazy() {
if (_handle) {
_handle.destroy();
}
}
};
template<std::movable T>
Lazy<T>::promise_type::~promise_type() = default;
// signal the completion of work without any result value
struct Done {};
using Work = Lazy<Done>;
}
|