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 Promise &operator=(const Promise<T> &other) = delete;
69 Promise &operator=(Promise<T> &&other) noexcept {
70 if (this == &other) {
71 return *this;
72 }
73 if (core_ != nullptr) {
74 core_->destroyCoroutine();
75 }
76 core_ = other.core_;
77 other.core_ = nullptr;
78 return *this;
79 }
80 Promise(const Promise<T> &other) = delete;
82 if (core_ != nullptr) {
83 core_->destroyCoroutine();
84 }
85 }
86
89 PromiseAwaiter_ operator co_await() const { return PromiseAwaiter_{*core_}; }
90
92 [[nodiscard]] bool ready() const { return core_->ready(); }
93
94 T unwrap() {
95 if (ready()) {
96 auto &slot = core_->slot.value();
97 switch (slot.index()) {
98 case 0: {
99 T value = std::move(std::get<0>(slot));
100 core_->slot.reset();
101 return value;
102 }
103 case 1: {
104 std::rethrow_exception(std::get<1>(slot));
105 }
106 default:
107 throw UvcoException("PromiseAwaiter_::await_resume: invalid slot");
108 }
109 }
110 throw UvcoException("unwrap called on unfulfilled promise");
111 }
112
113protected:
128 ~PromiseAwaiter_() = default;
129
132 [[nodiscard]] bool await_ready() const {
133 return core_.ready() && !core_.stale();
134 }
135
138 [[nodiscard]] std::coroutine_handle<>
139 await_suspend(std::coroutine_handle<> handle) const {
140 BOOST_ASSERT_MSG(!core_.isAwaited(),
141 "promise is already being waited on!");
142 core_.setHandle(handle);
143 return Loop::getNext();
144 }
145
147 T await_resume() const {
148 if (core_.stale()) {
149 throw UvcoException(
150 "co_await called on previously finished promise (T)");
151 }
152 if (core_.slot.has_value()) {
153 switch (core_.slot->index()) {
154 case 0: {
155 T result = std::move(std::get<0>(core_.slot.value()));
156 core_.slot.reset();
157 return result;
158 }
159 case 1: {
160 const auto exc = std::get<1>(core_.slot.value());
161 core_.slot.reset();
162 std::rethrow_exception(exc);
163 }
164 default:
165 throw UvcoException("PromiseAwaiter_::await_resume: invalid slot");
166 }
167 }
168 throw UvcoException("await_resume called on unfulfilled promise (bug?)");
169 }
170
172 };
173
174 template <typename U> friend class Coroutine;
175 template <typename... Ts> friend class SelectSet;
176
177 // Constructor used by Coroutine<T>
179
180 PromiseCore_ *core() { return core_; }
181
183};
184
193template <> class Promise<void> {
194 struct PromiseAwaiter_;
196
197public:
201
203 Promise(Promise<void> &&other) noexcept;
204 Promise &operator=(const Promise<void> &other) = delete;
205 Promise &operator=(Promise<void> &&other) noexcept;
206 Promise(const Promise<void> &other) = delete;
207 ~Promise();
208
211 PromiseAwaiter_ operator co_await() const;
212
214 [[nodiscard]] bool ready() const;
215
216 // Get the result *right now*, and throw an exception if the promise
217 // is not ready, or if it encountered an exception itself.
218 void unwrap();
219
220private:
230 ~PromiseAwaiter_() = default;
231
234 [[nodiscard]] bool await_ready() const;
237 [[nodiscard]] std::coroutine_handle<>
238 await_suspend(std::coroutine_handle<> handle) const;
239 void await_resume() const;
240
242 };
243
245
246 friend class Coroutine<void>;
247 template <typename... Ts> friend class SelectSet;
248
250 BOOST_ASSERT(core_ != nullptr);
251 return core_;
252 }
253
255};
256
261template <typename T> class Coroutine {
265
266public:
293 template <typename... Args> explicit Coroutine(Args &&.../* args */) {
294 // Assert statically that no arg is an rvalue/xvalue reference
295 static_assert(
296 (!std::is_rvalue_reference_v<Args &&> && ...),
297 "Coroutine constructor arguments must not be rvalue references");
298 }
299
302 Coroutine() = default;
303 Coroutine(const Coroutine &other) = delete;
304 Coroutine &operator=(const Coroutine &other) = delete;
305 Coroutine(Coroutine &&other) = delete;
306 Coroutine &operator=(Coroutine &&other) = delete;
307 ~Coroutine() = default;
308
312
315 void return_value(T value) {
316 // Probably cancelled.
317 if (core_.slot.has_value() && core_.slot->index() == 1) {
318 return;
319 }
320 BOOST_ASSERT(!core_.slot);
321 core_.slot = std::move(value);
322 core_.resume();
323 }
324
332 // Note: if suspend_always is chosen, we can better control when the promise
333 // will be scheduled.
334 std::suspend_never initial_suspend() noexcept {
335 core_.setRunning(std::coroutine_handle<Coroutine<T>>::from_promise(*this));
336 return {};
337 }
338
340 std::suspend_always final_suspend() noexcept { return {}; }
341
342 // Part of the coroutine protocol: called upon unhandled exception leaving
343 // the coroutine.
345 core_.except(std::current_exception());
346 core_.resume();
347 }
348
349protected:
351};
352
353template <> class Coroutine<void> {
355 using SharedCore_ = PromiseCore_ *;
356
357public:
358 template <typename... Args> explicit Coroutine(Args &&.../* args */) {
359 // Assert statically that no arg is an rvalue/xvalue reference
360 static_assert(
361 (!std::is_rvalue_reference_v<Args &&> && ...),
362 "Coroutine constructor arguments must not be rvalue references");
363 }
364
365 Coroutine() = default;
366 // Coroutine is pinned in memory and not allowed to copy/move.
367 Coroutine(Coroutine<void> &&other) noexcept = delete;
368 Coroutine &operator=(const Coroutine<void> &other) = delete;
369 Coroutine &operator=(Coroutine<void> &&other) = delete;
370 Coroutine(const Coroutine<void> &other) = delete;
371 ~Coroutine() = default;
372
374 Promise<void> get_return_object() { return Promise<void>{core_}; }
377 std::suspend_never initial_suspend() noexcept {
378 core_.setRunning(
379 std::coroutine_handle<Coroutine<void>>::from_promise(*this));
380 return {};
381 }
387 std::suspend_always final_suspend() noexcept { return {}; }
388
391 void return_void();
394 void unhandled_exception();
395
396private:
398};
399
401
402} // namespace uvco
Definition promise.h:261
void unhandled_exception()
Definition promise.h:344
std::suspend_always final_suspend() noexcept
Definition promise.h:340
Coroutine()=default
std::suspend_never initial_suspend() noexcept
Definition promise.h:334
~Coroutine()=default
Coroutine(const Coroutine &other)=delete
void return_value(T value)
Definition promise.h:315
Coroutine & operator=(const Coroutine &other)=delete
Promise< T > get_return_object()
Definition promise.h:311
Coroutine(Coroutine &&other)=delete
PromiseCore_ * SharedCore_
Definition promise.h:264
PromiseCore< T > core_
Definition promise.h:350
Coroutine & operator=(Coroutine &&other)=delete
Coroutine(Args &&...)
Definition promise.h:293
PromiseCore< T > PromiseCore_
PromiseCore_ handles the inner mechanics of resumption and suspension.
Definition promise.h:263
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:249
PromiseCore< void > PromiseCore_
Definition promise.h:195
PromiseCore< void > * core_
Definition promise.h:254
Coroutine< void > promise_type
Definition promise.h:200
friend class SelectSet
Definition promise.h:247
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:69
friend class Coroutine
Definition promise.h:174
~Promise()
Definition promise.h:81
Promise & operator=(const Promise< T > &other)=delete
Promise(PromiseCore_ &core)
Definition promise.h:178
T unwrap()
Definition promise.h:94
bool ready() const
Returns if promise has been fulfilled.
Definition promise.h:92
friend class SelectSet
Definition promise.h:175
PromiseCore_ * core()
Definition promise.h:180
Coroutine< T > promise_type
Definition promise.h:63
PromiseCore_ * core_
Definition promise.h:182
Definition async_work.cc:18
Definition promise.h:118
T await_resume() const
Definition promise.h:147
PromiseAwaiter_ & operator=(PromiseAwaiter_ &&)=delete
PromiseCore_ & core_
Definition promise.h:171
PromiseAwaiter_ & operator=(const PromiseAwaiter_ &)=delete
PromiseAwaiter_(PromiseCore_ &core)
Definition promise.h:123
PromiseAwaiter_(const PromiseAwaiter_ &)=delete
std::coroutine_handle await_suspend(std::coroutine_handle<> handle) const
Definition promise.h:139
PromiseAwaiter_(PromiseAwaiter_ &&)=delete
bool await_ready() const
Definition promise.h:132
Handles the actual suspension and resumption.
Definition promise.h:222
PromiseAwaiter_ & operator=(PromiseAwaiter_ &&)=delete
PromiseAwaiter_(const PromiseAwaiter_ &)=delete
PromiseCore< void > & core_
Definition promise.h:241
PromiseAwaiter_ & operator=(const PromiseAwaiter_ &)=delete
PromiseAwaiter_(PromiseAwaiter_ &&)=delete
PromiseAwaiter_(PromiseCore< void > &core)
Definition promise.cc:14
Definition exception.h:19