uvco 0.1
Loading...
Searching...
No Matches
multipromise.h
Go to the documentation of this file.
1// uvco (c) 2024 Lewin Bormann. See LICENSE for specific terms.
2
3#pragma once
4
5#include "uvco/exception.h"
6#include "uvco/loop/loop.h"
9
10#include <coroutine>
11#include <exception>
12#include <memory>
13#include <optional>
14
15namespace uvco {
16
19
24template <typename T> class MultiPromiseCore : public PromiseCore<T> {
26
27public:
28 MultiPromiseCore() = default;
33 static_assert(!std::is_void_v<T>);
34 ~MultiPromiseCore() override = default;
35
42 void setHandle(std::coroutine_handle<> handle) override {
43 // Once an external scheduler works, Promises will not be nested anymore
44 // (resume called by resume down in the stack)
45 //
46 // BOOST_ASSERT(PromiseCore<T>::state_
47 // == PromiseState::init || PromiseCore<T>::state_ ==
48 // PromiseState::finished);
49 //
50 // state is init or running (latter can occur if setHandle is called from a
51 // stack originating at resume()).
53 "MultiPromise must be co_awaited before next yield");
54 BOOST_ASSERT_MSG(!PromiseCore<T>::waitingHandle_,
55 "MultiPromise must be co_awaited before next yield");
58 }
59
67 if (suspended_) {
68 BOOST_ASSERT(coroutine_ != nullptr);
69 suspended_ = false;
71 }
72 }
73
80 BOOST_ASSERT(!suspended_);
81 suspended_ = true;
82 }
83
91 if (coroutine_ != nullptr) {
92 const std::coroutine_handle<> coroutine = coroutine_;
93 coroutine_ = nullptr;
94 Loop::cancel(coroutine);
95 // Careful: within this function, this class' dtor is called!
96 coroutine.destroy();
97 }
98 }
99
104 void setTerminated() { terminated_ = true; }
105
107 [[nodiscard]] bool isTerminated() const { return terminated_; }
108
109private:
111 bool suspended_ = false;
114 bool terminated_ = false;
115};
116
117template <typename T> class Generator;
118
127template <typename T> class MultiPromise {
128protected:
130
132
133public:
139 static_assert(!std::is_void_v<T>);
140
142 MultiPromise(MultiPromise<T> &&other) noexcept : core_{other.core_} {
143 other.core_ = nullptr;
144 }
147 if (this == &other) {
148 return *this;
149 }
150 if (core_ != nullptr) {
151 core_->destroyCoroutine();
152 }
153 core_ = other.core_;
154 other.core_ = nullptr;
155 return *this;
156 }
157 MultiPromise(const MultiPromise<T> &other) = delete;
159 if (core_ != nullptr) {
160 core_->cancelGenerator();
161 }
162 core_ = nullptr;
163 }
164
167 Promise<std::optional<T>> next() { co_return (co_await *this); }
168
174 MultiPromiseAwaiter_ operator co_await() const {
176 }
177
180 bool ready() {
181 return core_ == nullptr || core_->slot.has_value() || core_->isTerminated();
182 }
183
198 void cancel() {
199 if (core_ == nullptr) {
200 return;
201 }
202 core_->cancelGenerator();
203 core_ = nullptr;
204 }
205
206protected:
211 constexpr explicit MultiPromiseAwaiter_(PromiseCore_ *core) : core_{core} {}
217
220 [[nodiscard]] bool await_ready() const {
221 return core_ == nullptr || core_->isTerminated() ||
222 core_->slot.has_value();
223 }
224
226 [[nodiscard]] std::coroutine_handle<>
227 await_suspend(std::coroutine_handle<> handle) const {
228 if (core_ == nullptr) {
229 return Loop::getNext();
230 }
231 BOOST_ASSERT_MSG(!core_->isAwaited(),
232 "promise is already being waited on!\n");
233 core_->setHandle(handle);
234 core_->resumeGenerator();
235 return Loop::getNext();
236 }
237
243 std::optional<T> await_resume() const {
244 if (core_ == nullptr) {
245 return std::nullopt;
246 }
247 if (!core_->slot) {
248 // Terminated by co_return
249 return std::nullopt;
250 }
251 switch (core_->slot->index()) {
252 [[likely]] case 0: {
253 std::optional<T> result = std::move(std::get<0>(core_->slot.value()));
254 core_->slot.reset();
255 return std::move(result);
256 }
257 case 1:
258 // Terminated by exception
259 BOOST_ASSERT(core_->isTerminated());
260 std::rethrow_exception(std::get<1>(core_->slot.value()));
261 default:
262 throw UvcoException("MultiPromiseAwaiter_::await_resume: invalid slot");
263 }
264 }
265
267 };
268
269 template <typename U> friend class Generator;
270
271 explicit MultiPromise(PromiseCore_ &core) : core_{&core} {}
272
274};
275
278template <typename T> class Generator {
279 struct YieldAwaiter_;
281
282public:
283 // A generator promise object is pinned in memory (copy/move forbidden).
284 Generator() = default;
285 Generator(Generator<T> &&) noexcept = delete;
286 Generator &operator=(const Generator<T> &) = delete;
287 Generator &operator=(Generator<T> &&) noexcept = delete;
288 Generator(const Generator<T> &other) = delete;
289 ~Generator() = default;
290
293
296 void return_void() {
297 core_.setTerminated();
298 core_.resume();
299 }
300
302 // Note: if suspend_always is chosen, we can better control when the
303 // MultiPromise will be scheduled.
304 std::suspend_never initial_suspend() noexcept {
305 core_.setRunning(std::coroutine_handle<Generator<T>>::from_promise(*this));
306 return {};
307 }
308
310 std::suspend_always final_suspend() noexcept { return {}; }
311
314 core_.slot = std::current_exception();
315 core_.setTerminated();
316 core_.resume();
317 }
318
330 BOOST_ASSERT(!core_.slot);
331 core_.slot = std::move(value);
332 core_.resume();
333 return YieldAwaiter_{core_};
334 }
335
336private:
341 explicit YieldAwaiter_(PromiseCore_ &core) : core_{core} {}
342
343 [[nodiscard]] bool await_ready() const { return !core_.slot.has_value(); }
344
345 bool await_suspend(std::coroutine_handle<> /* handle */) {
346 // The core_ already knows our handle from initial_suspend().
347 core_.suspendGenerator();
348 return true;
349 }
350
352 // Returning into the generator coroutine
353 }
354
356 };
357
359};
360
362
363} // namespace uvco
Definition multipromise.h:278
Generator(Generator< T > &&) noexcept=delete
void unhandled_exception()
Part of the coroutine protocol (see Promise).
Definition multipromise.h:313
MultiPromise< T > get_return_object()
A generator (yielding) coroutine returns a MultiPromise.
Definition multipromise.h:292
PromiseCore_ core_
Definition multipromise.h:358
std::suspend_always final_suspend() noexcept
Part of the coroutine protocol (see Promise).
Definition multipromise.h:310
void return_void()
Definition multipromise.h:296
MultiPromiseCore< T > PromiseCore_
Definition multipromise.h:280
YieldAwaiter_ yield_value(T value)
Definition multipromise.h:329
Generator()=default
std::suspend_never initial_suspend() noexcept
Part of the coroutine protocol (see Promise).
Definition multipromise.h:304
static void cancel(std::coroutine_handle<> handle)
Definition loop.cc:104
static std::coroutine_handle getNext()
Definition loop.cc:108
static void enqueue(std::coroutine_handle<> handle)
Definition loop.cc:94
Definition multipromise.h:24
MultiPromiseCore(MultiPromiseCore &&)=delete
MultiPromiseCore(const MultiPromiseCore &)=delete
void setTerminated()
Definition multipromise.h:104
void suspendGenerator()
Definition multipromise.h:79
void cancelGenerator()
Definition multipromise.h:89
void setHandle(std::coroutine_handle<> handle) override
Definition multipromise.h:42
MultiPromiseCore & operator=(MultiPromiseCore &&)=delete
bool suspended_
True if the generator is suspended at a co_yield point.
Definition multipromise.h:111
bool isTerminated() const
Check if the generator has been cancelled or has returned.
Definition multipromise.h:107
~MultiPromiseCore() override=default
bool terminated_
Definition multipromise.h:114
MultiPromiseCore & operator=(const MultiPromiseCore &)=delete
void resumeGenerator()
Definition multipromise.h:65
Definition multipromise.h:127
Promise< std::optional< T > > next()
Definition multipromise.h:167
MultiPromise(PromiseCore_ &core)
Definition multipromise.h:271
MultiPromise & operator=(const MultiPromise< T > &)=delete
MultiPromise(MultiPromise< T > &&other) noexcept
An unfulfilled MultiPromise.
Definition multipromise.h:142
MultiPromise & operator=(MultiPromise< T > &&other) noexcept
Definition multipromise.h:146
MultiPromiseCore< T > PromiseCore_
Definition multipromise.h:131
friend class Generator
Definition multipromise.h:269
~MultiPromise()
Definition multipromise.h:158
Generator< T > promise_type
Definition multipromise.h:136
MultiPromise(const MultiPromise< T > &other)=delete
void cancel()
Definition multipromise.h:198
bool ready()
Definition multipromise.h:180
PromiseCore_ * core_
Definition multipromise.h:273
std::coroutine_handle coroutine_
Definition promise_core.h:129
PromiseCore()=default
PromiseState state_
Definition promise_core.h:135
std::coroutine_handle waitingHandle_
Definition promise_core.h:132
Definition promise.h:49
@ waitedOn
Definition promise_core.h:37
@ finished
Definition promise_core.h:40
Definition async_work.cc:18
Definition multipromise.h:340
PromiseCore_ & core_
Definition multipromise.h:355
void await_resume()
Definition multipromise.h:351
bool await_suspend(std::coroutine_handle<>)
Definition multipromise.h:345
YieldAwaiter_(PromiseCore_ &core)
Definition multipromise.h:341
bool await_ready() const
Definition multipromise.h:343
Definition multipromise.h:210
MultiPromiseAwaiter_ & operator=(const MultiPromiseAwaiter_ &)=delete
MultiPromiseAwaiter_(MultiPromiseAwaiter_ &&)=delete
std::optional< T > await_resume() const
Definition multipromise.h:243
PromiseCore_ * core_
Definition multipromise.h:266
constexpr MultiPromiseAwaiter_(PromiseCore_ *core)
Definition multipromise.h:211
MultiPromiseAwaiter_ & operator=(MultiPromiseAwaiter_ &&)=delete
MultiPromiseAwaiter_(const MultiPromiseAwaiter_ &)=delete
bool await_ready() const
Definition multipromise.h:220
std::coroutine_handle await_suspend(std::coroutine_handle<> handle) const
Definition multipromise.h:227
Definition exception.h:19