uvco 0.1
Loading...
Searching...
No Matches
promise_core.h
Go to the documentation of this file.
1// uvco (c) 2023 Lewin Bormann. See LICENSE for specific terms.
2
3#pragma once
4
5#include "uvco/exception.h"
7#include "uvco/loop/loop.h"
8
9#include <boost/assert.hpp>
10#include <fmt/core.h>
11#include <fmt/format.h>
12
13#include <coroutine>
14#include <cstdio>
15#include <exception>
16#include <optional>
17#include <typeinfo>
18#include <utility>
19#include <uv.h>
20#include <variant>
21
22namespace uvco {
25
34enum class PromiseState {
37 init = 0,
40 waitedOn = 1,
43 resuming = 2,
46 finished = 3,
47};
48
61template <typename T> class PromiseCore : public RefCounted<PromiseCore<T>> {
62public:
63 PromiseCore() = default;
64
65 // The promise core is pinned in memory until the coroutine has finished.
66 PromiseCore(const PromiseCore &) = delete;
68 PromiseCore &operator=(const PromiseCore &) = delete;
70 explicit PromiseCore(T &&value)
71 : slot{std::move(value)}, state_{PromiseState::finished} {}
72
74 virtual void setHandle(std::coroutine_handle<> handle) {
76 throw UvcoException("PromiseCore is already awaited or has finished");
77 }
78 handle_ = handle;
80 }
81
84 void resetHandle() {
85 BOOST_ASSERT((state_ == PromiseState::waitedOn && handle_) ||
89 handle_.reset();
91 }
92 }
93
97 void cancel() {
99 BOOST_ASSERT(!ready());
100 // Fill the slot with an exception, so that the coroutine can be resumed.
101 // Double-check `if` for release builds.
102 if (!slot) {
103 slot = std::make_exception_ptr(
104 UvcoException(UV_ECANCELED, "Promise cancelled"));
105 }
106 resume();
107 }
108 // else: the underlying coroutine has already returned, so there is no need
109 // to cancel it.
110 }
111
113 bool willResume() { return handle_.has_value(); }
115 [[nodiscard]] bool ready() const { return slot.has_value(); }
118 [[nodiscard]] bool stale() const {
119 return state_ == PromiseState::finished && !ready();
120 }
121
126 virtual void resume() {
127 if (handle_) {
128 BOOST_ASSERT(state_ == PromiseState::waitedOn);
130 auto resume = *handle_;
131 handle_.reset();
133 } else {
134 // This occurs if no co_await has occured until resume. Either the
135 // promise was not co_awaited, or the producing coroutine immediately
136 // returned a value. (await_ready() == true)
137 }
138
139 switch (state_) {
141 // Coroutine returns but nobody has awaited yet. This is fine.
143 break;
145 // Not entirely correct, but the resumed awaiting coroutine is not coming
146 // back to us.
148 break;
150 // state is waitedOn, but no handle is set - that's an error.
151 BOOST_ASSERT_MSG(
152 false,
153 "PromiseCore::resume() called without handle in state waitedOn");
154 break;
156 // Happens in MultiPromiseCore on co_return if the co_awaiter has lost
157 // interest. Harmless if !handle_ (asserted above).
158 break;
159 }
160 }
161
165 virtual ~PromiseCore() {
167 fmt::print(
168 stderr,
169 "PromiseCore destroyed without ever being resumed ({}, state = {})\n",
170 typeid(T).name(), static_cast<int>(state_));
171 }
172 // This only happens if the awaiting coroutine has never been resumed, but
173 // the last promise provided by it is gone (in turn calling
174 // ~PromiseCore()). Important: we may only destroy a suspended coroutine,
175 // not a finished one.
176 if (handle_) {
177 handle_->destroy();
178 }
179 }
180
181 void except(const std::exception_ptr &exc) { slot = exc; }
182
184 std::optional<std::variant<T, std::exception_ptr>> slot;
185
186protected:
187 std::optional<std::coroutine_handle<>> handle_;
189};
190
194template <> class PromiseCore<void> : public RefCounted<PromiseCore<void>> {
195public:
196 PromiseCore() = default;
197 PromiseCore(const PromiseCore &) = delete;
201 ~PromiseCore() override;
202
204 void cancel();
205
207 void setHandle(std::coroutine_handle<> handle);
208
210 void resetHandle();
212 [[nodiscard]] bool willResume() const;
214 [[nodiscard]] bool ready() const;
216 [[nodiscard]] bool stale() const;
217
219 void resume();
220
222 void except(std::exception_ptr exc);
223
226 bool ready_ = false;
227
229 std::optional<std::exception_ptr> exception_;
230
231private:
232 std::optional<std::coroutine_handle<>> handle_;
234};
235
237
238} // namespace uvco
static void enqueue(std::coroutine_handle<> handle)
Definition loop.cc:73
PromiseCore(PromiseCore &&)=delete
std::optional< std::exception_ptr > exception_
Contains the exception if thrown.
Definition promise_core.h:229
PromiseCore(const PromiseCore &)=delete
std::optional< std::coroutine_handle<> > handle_
Definition promise_core.h:232
PromiseCore< void > & operator=(PromiseCore &&)=delete
PromiseCore< void > & operator=(const PromiseCore &)=delete
Definition promise_core.h:61
virtual ~PromiseCore()
Definition promise_core.h:165
PromiseCore(const PromiseCore &)=delete
bool ready() const
Checks if a value is present in the slot.
Definition promise_core.h:115
bool stale() const
Definition promise_core.h:118
std::optional< std::coroutine_handle<> > handle_
Definition promise_core.h:187
PromiseCore()=default
PromiseState state_
Definition promise_core.h:188
void except(const std::exception_ptr &exc)
Definition promise_core.h:181
PromiseCore(T &&value)
Definition promise_core.h:70
void cancel()
Definition promise_core.h:97
bool willResume()
Checks if a coroutine is waiting on a promise belonging to this core.
Definition promise_core.h:113
virtual void setHandle(std::coroutine_handle<> handle)
Set the coroutine to be resumed once a result is ready.
Definition promise_core.h:74
void resetHandle()
Definition promise_core.h:84
std::optional< std::variant< T, std::exception_ptr > > slot
The slot contains the result once obtained.
Definition promise_core.h:184
PromiseCore & operator=(PromiseCore &&)=delete
PromiseCore & operator=(const PromiseCore &)=delete
virtual void resume()
Definition promise_core.h:126
PromiseCore(PromiseCore &&)=delete
Definition internal_utils.h:98
PromiseState
Definition promise_core.h:34
Definition async_work.cc:17
Definition exception.h:19