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"
7
8#include <boost/assert.hpp>
9#include <fmt/format.h>
10
11#include <coroutine>
12#include <exception>
13#include <utility>
14
15namespace uvco {
16
19
20template <typename T> class Coroutine;
21template <typename T> class Promise;
22
49template <typename T> class Promise {
50protected:
51 struct PromiseAwaiter_;
54
55public:
64
65 Promise(Promise<T> &&other) noexcept : core_{other.core_} {
66 other.core_ = nullptr;
67 }
68
69 Promise &operator=(const Promise<T> &other) = delete;
70 Promise &operator=(Promise<T> &&other) noexcept {
71 if (this == &other) {
72 return *this;
73 }
74 if (core_ != nullptr) {
75 core_->destroyCoroutine();
76 }
77 core_ = other.core_;
78 other.core_ = nullptr;
79 return *this;
80 }
81 // A promise can be copied at low cost.
82 Promise(const Promise<T> &other) = delete;
84 if (core_ != nullptr) {
85 core_->destroyCoroutine();
86 }
87 }
88
91 PromiseAwaiter_ operator co_await() const { return PromiseAwaiter_{*core_}; }
92
94 [[nodiscard]] bool ready() const { return core_->ready(); }
95
96 T unwrap() {
97 if (ready()) {
98 auto &slot = core_->slot.value();
99 switch (slot.index()) {
100 case 0: {
101 T value = std::move(std::get<0>(slot));
102 core_->slot.reset();
103 return value;
104 }
105 case 1: {
106 std::rethrow_exception(std::get<1>(slot));
107 }
108 default:
109 throw UvcoException("PromiseAwaiter_::await_resume: invalid slot");
110 }
111 }
112 throw UvcoException("unwrap called on unfulfilled promise");
113 }
114
115protected:
130 ~PromiseAwaiter_() = default;
131
134 [[nodiscard]] bool await_ready() const {
135 return core_.ready() && !core_.stale();
136 }
137
140 [[nodiscard]] std::coroutine_handle<>
141 await_suspend(std::coroutine_handle<> handle) const {
142 BOOST_ASSERT_MSG(!core_.isAwaited(),
143 "promise is already being waited on!");
144 core_.setHandle(handle);
145 return Loop::getNext();
146 }
147
149 T await_resume() const {
150 if (core_.stale()) {
151 throw UvcoException(
152 "co_await called on previously finished promise (T)");
153 }
154 if (core_.slot.has_value()) {
155 switch (core_.slot->index()) {
156 case 0: {
157 T result = std::move(std::get<0>(core_.slot.value()));
158 core_.slot.reset();
159 return result;
160 }
161 case 1: {
162 const auto exc = std::get<1>(core_.slot.value());
163 core_.slot.reset();
164 std::rethrow_exception(exc);
165 }
166 default:
167 throw UvcoException("PromiseAwaiter_::await_resume: invalid slot");
168 }
169 }
170 throw UvcoException("await_resume called on unfulfilled promise (bug?)");
171 }
172
174 };
175
176 template <typename U> friend class Coroutine;
177 template <typename... Ts> friend class SelectSet;
178
179 // Constructor used by Coroutine<T>
181
182 PromiseCore_ *core() { return core_; }
183
185};
186
195template <> class Promise<void> {
196 struct PromiseAwaiter_;
198
199public:
203
205 Promise(Promise<void> &&other) noexcept;
206 Promise &operator=(const Promise<void> &other) = delete;
207 Promise &operator=(Promise<void> &&other) noexcept;
208 Promise(const Promise<void> &other) = delete;
209 ~Promise();
210
213 PromiseAwaiter_ operator co_await() const;
214
216 [[nodiscard]] bool ready() const;
217
218 // Get the result *right now*, and throw an exception if the promise
219 // is not ready, or if it encountered an exception itself.
220 void unwrap();
221
222private:
232 ~PromiseAwaiter_() = default;
233
236 [[nodiscard]] bool await_ready() const;
239 [[nodiscard]] std::coroutine_handle<>
240 await_suspend(std::coroutine_handle<> handle) const;
241 void await_resume() const;
242
244 };
245
247
248 friend class Coroutine<void>;
249 template <typename... Ts> friend class SelectSet;
250
252 BOOST_ASSERT(core_ != nullptr);
253 return core_;
254 }
255
257};
258
263template <typename T> class Coroutine {
267
268public:
295 template <typename... Args> explicit Coroutine(Args &&.../* args */) {
296 // Assert statically that no arg is an rvalue/xvalue reference
297 static_assert(
298 (!std::is_rvalue_reference_v<Args &&> && ...),
299 "Coroutine constructor arguments must not be rvalue references");
300 }
301
304 Coroutine() = default;
305 Coroutine(const Coroutine &other) = delete;
306 Coroutine &operator=(const Coroutine &other) = delete;
307 Coroutine(Coroutine &&other) = delete;
308 Coroutine &operator=(Coroutine &&other) = delete;
309 ~Coroutine() = default;
310
314
317 void return_value(T value) {
318 // Probably cancelled.
319 if (core_.slot.has_value() && core_.slot->index() == 1) {
320 return;
321 }
322 BOOST_ASSERT(!core_.slot);
323 core_.slot = std::move(value);
324 core_.resume();
325 }
326
334 // Note: if suspend_always is chosen, we can better control when the promise
335 // will be scheduled.
336 std::suspend_never initial_suspend() noexcept {
337 core_.setRunning(std::coroutine_handle<Coroutine<T>>::from_promise(*this));
338 return {};
339 }
340
342 std::suspend_always final_suspend() noexcept { return {}; }
343
344 // Part of the coroutine protocol: called upon unhandled exception leaving
345 // the coroutine.
347 core_.except(std::current_exception());
348 core_.resume();
349 }
350
351protected:
353};
354
355template <> class Coroutine<void> {
357 using SharedCore_ = PromiseCore_ *;
358
359public:
360 template <typename... Args> explicit Coroutine(Args &&.../* args */) {
361 // Assert statically that no arg is an rvalue/xvalue reference
362 static_assert(
363 (!std::is_rvalue_reference_v<Args &&> && ...),
364 "Coroutine constructor arguments must not be rvalue references");
365 }
366
367 Coroutine() = default;
368 // Coroutine is pinned in memory and not allowed to copy/move.
369 Coroutine(Coroutine<void> &&other) noexcept = delete;
370 Coroutine &operator=(const Coroutine<void> &other) = delete;
371 Coroutine &operator=(Coroutine<void> &&other) = delete;
372 Coroutine(const Coroutine<void> &other) = delete;
373 ~Coroutine() = default;
374
376 Promise<void> get_return_object() { return Promise<void>{core_}; }
379 std::suspend_never initial_suspend() noexcept {
380 core_.setRunning(
381 std::coroutine_handle<Coroutine<void>>::from_promise(*this));
382 return {};
383 }
389 std::suspend_always final_suspend() noexcept { return {}; }
390
393 void return_void();
396 void unhandled_exception();
397
398private:
400};
401
403
404} // namespace uvco
Definition promise.h:263
void unhandled_exception()
Definition promise.h:346
std::suspend_always final_suspend() noexcept
Definition promise.h:342
Coroutine()=default
std::suspend_never initial_suspend() noexcept
Definition promise.h:336
~Coroutine()=default
Coroutine(const Coroutine &other)=delete
void return_value(T value)
Definition promise.h:317
Coroutine & operator=(const Coroutine &other)=delete
Promise< T > get_return_object()
Definition promise.h:313
Coroutine(Coroutine &&other)=delete
PromiseCore_ * SharedCore_
Definition promise.h:266
PromiseCore< T > core_
Definition promise.h:352
Coroutine & operator=(Coroutine &&other)=delete
Coroutine(Args &&...)
Definition promise.h:295
PromiseCore< T > PromiseCore_
PromiseCore_ handles the inner mechanics of resumption and suspension.
Definition promise.h:265
static std::coroutine_handle getNext()
Definition loop.cc:108
Definition promise_core.h:45
Promise(const Promise< void > &other)=delete
Promise & operator=(const Promise< void > &other)=delete
Promise(Promise< void > &&other) noexcept
Promise ready to be awaited or fulfilled.
Definition promise.cc:46
PromiseCore< void > * core()
Definition promise.h:251
PromiseCore< void > PromiseCore_
Definition promise.h:197
PromiseCore< void > * core_
Definition promise.h:256
Coroutine< void > promise_type
Definition promise.h:202
friend class SelectSet
Definition promise.h:249
Definition promise.h:49
Promise(Promise< T > &&other) noexcept
Definition promise.h:65
Promise(const Promise< T > &other)=delete
PromiseCore< T > PromiseCore_
PromiseCore_ handles the inner mechanics of resumption and suspension.
Definition promise.h:53
Promise & operator=(Promise< T > &&other) noexcept
Definition promise.h:70
friend class Coroutine
Definition promise.h:176
~Promise()
Definition promise.h:83
Promise & operator=(const Promise< T > &other)=delete
A promise can be copied at low cost.
Promise(PromiseCore_ &core)
Definition promise.h:180
T unwrap()
Definition promise.h:96
bool ready() const
Returns if promise has been fulfilled.
Definition promise.h:94
friend class SelectSet
Definition promise.h:177
PromiseCore_ * core()
Definition promise.h:182
Coroutine< T > promise_type
Definition promise.h:63
PromiseCore_ * core_
Definition promise.h:184
Definition async_work.cc:18
Definition promise.h:120
T await_resume() const
Definition promise.h:149
PromiseAwaiter_ & operator=(PromiseAwaiter_ &&)=delete
PromiseCore_ & core_
Definition promise.h:173
PromiseAwaiter_ & operator=(const PromiseAwaiter_ &)=delete
PromiseAwaiter_(PromiseCore_ &core)
Definition promise.h:125
PromiseAwaiter_(const PromiseAwaiter_ &)=delete
std::coroutine_handle await_suspend(std::coroutine_handle<> handle) const
Definition promise.h:141
PromiseAwaiter_(PromiseAwaiter_ &&)=delete
bool await_ready() const
Definition promise.h:134
Handles the actual suspension and resumption.
Definition promise.h:224
PromiseAwaiter_ & operator=(PromiseAwaiter_ &&)=delete
PromiseAwaiter_(const PromiseAwaiter_ &)=delete
PromiseCore< void > & core_
Definition promise.h:243
PromiseAwaiter_ & operator=(const PromiseAwaiter_ &)=delete
PromiseAwaiter_(PromiseAwaiter_ &&)=delete
PromiseAwaiter_(PromiseCore< void > &core)
Definition promise.cc:14
Definition exception.h:19