uvco 0.1
Loading...
Searching...
No Matches
promise.h
Go to the documentation of this file.
1// uvco (c) 2023-2024 Lewin Bormann. See LICENSE for specific terms.
2
3#pragma once
4
5#include "uvco/exception.h"
8
9#include <boost/assert.hpp>
10#include <fmt/format.h>
11
12#include <coroutine>
13#include <exception>
14#include <utility>
15
16namespace uvco {
17
20
21template <typename T> class Coroutine;
22template <typename T> class Promise;
23
29template <typename T> class PromiseHandle {
30public:
31 PromiseHandle(const PromiseHandle &) = delete;
36 if (core_ != nullptr) {
37 core_->delRef();
38 }
39 }
40
43 void cancel() {
44 if (core_ != nullptr) {
45 core_->cancel();
46 }
47 }
48
49private:
50 explicit PromiseHandle(PromiseCore<T> *core) : core_{core->addRef()} {}
51
52 friend class Promise<T>;
54};
55
76template <typename T> class Promise {
77protected:
78 struct PromiseAwaiter_;
82
83public:
92
96 explicit Promise(T &&result)
97 : core_{makeRefCounted<PromiseCore_>(std::move(result))} {}
98
99 Promise(Promise<T> &&other) noexcept : core_{other.core_} {
100 other.core_ = nullptr;
101 }
103 Promise &operator=(const Promise<T> &other) {
104 if (this == &other) {
105 return *this;
106 }
107 core_ = other.core_->addRef();
108 return *this;
109 }
110 Promise &operator=(Promise<T> &&other) noexcept {
111 if (this == &other) {
112 return *this;
113 }
114 if (core_ != nullptr) {
115 core_->delRef();
116 }
117 core_ = other.core_;
118 other.core_ = nullptr;
119 return *this;
120 }
121 // A promise can be copied at low cost.
122 Promise(const Promise<T> &other) : core_{other.core_->addRef()} {}
124 if (core_ != nullptr) {
125 core_->delRef();
126 }
127 }
128
131
134 PromiseAwaiter_ operator co_await() const { return PromiseAwaiter_{*core_}; }
135
137 [[nodiscard]] bool ready() const { return core_->ready(); }
138
139 T unwrap() {
140 if (ready()) {
141 auto &slot = core_->slot.value();
142 switch (slot.index()) {
143 case 0: {
144 T value = std::move(std::get<0>(slot));
145 core_->slot.reset();
146 return std::move(value);
147 }
148 case 1: {
149 std::rethrow_exception(std::get<1>(slot));
150 }
151 default:
152 throw UvcoException("PromiseAwaiter_::await_resume: invalid slot");
153 }
154 }
155 throw UvcoException("unwrap called on unfulfilled promise");
156 }
157
158protected:
173 ~PromiseAwaiter_() = default;
174
177 [[nodiscard]] bool await_ready() const { return core_.ready(); }
180 [[nodiscard]] bool await_suspend(std::coroutine_handle<> handle) const {
181 BOOST_ASSERT_MSG(!core_.willResume(),
182 "promise is already being waited on!");
184 return true;
185 }
188 T await_resume() const {
189 if (core_.stale()) {
190 throw UvcoException(
191 "co_await called on previously finished promise (T)");
192 }
193 if (core_.slot.has_value()) {
194 switch (core_.slot->index()) {
195 case 0: {
196 T result = std::move(std::get<0>(core_.slot.value()));
197 core_.slot.reset();
198 return std::move(result);
199 }
200 case 1:
201 std::rethrow_exception(std::get<1>(core_.slot.value()));
202 default:
203 throw UvcoException("PromiseAwaiter_::await_resume: invalid slot");
204 }
205 }
206 throw UvcoException("await_resume called on unfulfilled promise (bug?)");
207 }
208
210 };
211
212 template <typename U> friend class Coroutine;
213 template <typename... Ts> friend class SelectSet;
214
215 explicit Promise(SharedCore_ core) : core_{core->addRef()} {}
216 SharedCore_ &core() { return core_; }
217
219};
220
224template <> class Promise<void> {
225 struct PromiseAwaiter_;
227
228public:
232
234 Promise();
235 Promise(Promise<void> &&other) noexcept;
236 Promise &operator=(const Promise<void> &other);
237 Promise &operator=(Promise<void> &&other) noexcept;
238 Promise(const Promise<void> &other);
239 ~Promise();
240
242
245 PromiseAwaiter_ operator co_await() const;
246
248 [[nodiscard]] bool ready() const;
249
250 // Get the result *right now*, and throw an exception if the promise
251 // is not ready, or if it encountered an exception itself.
252 void unwrap();
253
254private:
256 struct PromiseAwaiter_ {
264
267 [[nodiscard]] bool await_ready() const;
270 [[nodiscard]] bool await_suspend(std::coroutine_handle<> handle) const;
271 void await_resume() const;
272
274 };
275
277
278 friend class Coroutine<void>;
279 template <typename... Ts> friend class SelectSet;
280
281 SharedCore_ &core() { return core_; }
282
284};
285
287template <typename T> class Coroutine {
291
292public:
296 Coroutine(const Coroutine &other) = delete;
297 Coroutine &operator=(const Coroutine &other) = delete;
298 Coroutine(Coroutine &&other) = delete;
299 Coroutine &operator=(Coroutine &&other) = delete;
300
302 if (core_ != nullptr) {
303 core_->delRef();
304 }
305 }
306
310
313 void return_value(T value) {
314 // Probably cancelled.
315 if (core_->slot.has_value() && core_->slot->index() == 1) {
316 return;
317 }
318 BOOST_ASSERT(!core_->slot);
319 core_->slot = std::move(value);
320 core_->resume();
321 }
322
330 // Note: if suspend_always is chosen, we can better control when the promise
331 // will be scheduled.
332 std::suspend_never initial_suspend() noexcept { return {}; }
335 std::suspend_never final_suspend() noexcept { return {}; }
336
337 // Part of the coroutine protocol: called upon unhandled exception leaving the
338 // coroutine.
340 core_->except(std::current_exception());
341 core_->resume();
342 }
343
344protected:
346};
347
348template <> class Coroutine<void> {
351
352public:
354 // Coroutine is pinned in memory and not allowed to copy/move.
355 Coroutine(Coroutine<void> &&other) noexcept = delete;
356 Coroutine &operator=(const Coroutine<void> &other) = delete;
358 Coroutine(const Coroutine<void> &other) = delete;
360 if (core_ != nullptr) {
361 core_->delRef();
362 }
363 }
364
369 std::suspend_never initial_suspend() noexcept { return {}; }
372 std::suspend_never final_suspend() noexcept { return {}; }
373
376 void return_void();
379 void unhandled_exception();
380
381private:
383};
384
386
387} // namespace uvco
Definition promise.h:348
Coroutine & operator=(Coroutine< void > &&other)=delete
Promise< void > get_return_object()
Part of the coroutine protocol.
Definition promise.h:366
~Coroutine()
Definition promise.h:359
Coroutine(Coroutine< void > &&other) noexcept=delete
Coroutine & operator=(const Coroutine< void > &other)=delete
SharedCore_ core_
Definition promise.h:382
Coroutine(const Coroutine< void > &other)=delete
Coroutine()
Definition promise.h:353
std::suspend_never final_suspend() noexcept
Definition promise.h:372
std::suspend_never initial_suspend() noexcept
Definition promise.h:369
A coroutine object used internally by C++20 coroutines ("promise object").
Definition promise.h:287
SharedCore_ core_
Definition promise.h:345
void unhandled_exception()
Definition promise.h:339
std::suspend_never initial_suspend() noexcept
Definition promise.h:332
std::suspend_never final_suspend() noexcept
Definition promise.h:335
~Coroutine()
Definition promise.h:301
Coroutine(const Coroutine &other)=delete
Coroutine()
Definition promise.h:295
void return_value(T value)
Definition promise.h:313
Coroutine & operator=(const Coroutine &other)=delete
Promise< T > get_return_object()
Definition promise.h:309
Coroutine(Coroutine &&other)=delete
Coroutine & operator=(Coroutine &&other)=delete
Definition promise_core.h:194
Definition promise_core.h:61
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
void except(const std::exception_ptr &exc)
Definition promise_core.h:181
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
std::optional< std::variant< T, std::exception_ptr > > slot
The slot contains the result once obtained.
Definition promise_core.h:184
virtual void resume()
Definition promise_core.h:126
Definition promise.h:29
PromiseCore< T > * core_
Definition promise.h:53
PromiseHandle(PromiseHandle &&)=delete
PromiseHandle & operator=(PromiseHandle &&)=delete
~PromiseHandle()
Definition promise.h:35
PromiseHandle & operator=(const PromiseHandle &)=delete
void cancel()
Definition promise.h:43
PromiseHandle(const PromiseHandle &)=delete
SharedCore_ & core()
Definition promise.h:281
SharedCore_ core_
Definition promise.h:283
Definition promise.h:76
Promise(T &&result)
Fulfilled promise; resolves immediately.
Definition promise.h:96
Promise(Promise< T > &&other) noexcept
Definition promise.h:99
SharedCore_ & core()
Definition promise.h:216
Promise & operator=(Promise< T > &&other) noexcept
Definition promise.h:110
Promise(const Promise< T > &other)
Definition promise.h:122
~Promise()
Definition promise.h:123
Promise & operator=(const Promise< T > &other)
A promise can be copied at low cost.
Definition promise.h:103
T unwrap()
Definition promise.h:139
Promise(SharedCore_ core)
Definition promise.h:215
Promise()
Unfulfilled, empty promise.
Definition promise.h:94
SharedCore_ core_
Definition promise.h:218
PromiseHandle< T > handle()
Return a handle that can be used to cancel the coroutine.
Definition promise.h:130
bool ready() const
Returns if promise has been fulfilled.
Definition promise.h:137
virtual T * addRef()
Definition internal_utils.h:109
virtual void delRef()
Definition internal_utils.h:117
Definition select.h:39
T * makeRefCounted(Args... args)
Create a new refcounted value. T must derive from RefCounted<T>.
Definition internal_utils.h:133
Definition async_work.cc:17
Definition promise.h:163
T await_resume() const
Definition promise.h:188
PromiseAwaiter_ & operator=(PromiseAwaiter_ &&)=delete
PromiseCore_ & core_
Definition promise.h:209
PromiseAwaiter_ & operator=(const PromiseAwaiter_ &)=delete
bool await_suspend(std::coroutine_handle<> handle) const
Definition promise.h:180
PromiseAwaiter_(PromiseCore_ &core)
Definition promise.h:168
PromiseAwaiter_(const PromiseAwaiter_ &)=delete
PromiseAwaiter_(PromiseAwaiter_ &&)=delete
bool await_ready() const
Definition promise.h:177
PromiseAwaiter_ & operator=(PromiseAwaiter_ &&)=delete
PromiseAwaiter_(const PromiseAwaiter_ &)=delete
PromiseCore< void > & core_
Definition promise.h:273
PromiseAwaiter_ & operator=(const PromiseAwaiter_ &)=delete
PromiseAwaiter_(PromiseAwaiter_ &&)=delete
Definition exception.h:19