uvco 0.1
Loading...
Searching...
No Matches
multipromise.h
Go to the documentation of this file.
1
2#pragma once
3
4#include "uvco/exception.h"
5#include "uvco/loop/loop.h"
8
9#include <coroutine>
10#include <exception>
11#include <memory>
12#include <optional>
13
14namespace uvco {
15
18
23template <typename T> class MultiPromiseCore : public PromiseCore<T> {
24public:
25 MultiPromiseCore() = default;
30 static_assert(!std::is_void_v<T>);
31 ~MultiPromiseCore() override = default;
32
39 void setHandle(std::coroutine_handle<> handle) override {
40 // Once an external scheduler works, Promises will not be nested anymore
41 // (resume called by resume down in the stack)
42 //
43 // BOOST_ASSERT(PromiseCore<T>::state_
44 // == PromiseState::init || PromiseCore<T>::state_ ==
45 // PromiseState::finished);
46 //
47 // state is init or running (latter can occur if setHandle is called from a
48 // stack originating at resume()).
50 "MultiPromise must be co_awaited before next yield");
51 BOOST_ASSERT_MSG(!PromiseCore<T>::handle_,
52 "MultiPromise must be co_awaited before next yield");
55 }
56
59 void resume() override { PromiseCore<T>::resume(); }
60
68 if (generatorHandle_) {
69 const auto generatorHandle = generatorHandle_.value();
70 generatorHandle_.reset();
71 Loop::enqueue(generatorHandle);
72 }
73 }
74
80 void suspendGenerator(std::coroutine_handle<> handle) {
81 BOOST_ASSERT_MSG(!generatorHandle_,
82 "MultiPromiseCore::suspendGenerator: generatorHandle_ is "
83 "already set");
84 generatorHandle_ = handle;
85 }
86
93 terminated();
94 if (generatorHandle_) {
95 const std::coroutine_handle<> generatorHandle = generatorHandle_.value();
96 generatorHandle_.reset();
97 // Careful: within this function, this class' dtor is called!
98 generatorHandle.destroy();
99 }
100 }
101
106 void terminated() { terminated_ = true; }
107
109 [[nodiscard]] bool isTerminated() const { return terminated_; }
110
111private:
113 std::optional<std::coroutine_handle<>> generatorHandle_;
116 bool terminated_ = false;
117};
118
119template <typename T> class Generator;
120
133template <typename T> class MultiPromise {
134protected:
136
138 using SharedCore_ = std::shared_ptr<PromiseCore_>;
139
140public:
146 static_assert(!std::is_void_v<T>);
147
149 MultiPromise(MultiPromise<T> &&) noexcept = default;
150 MultiPromise &operator=(const MultiPromise<T> &) = delete;
151 MultiPromise &operator=(MultiPromise<T> &&) noexcept = delete;
152 MultiPromise(const MultiPromise<T> &other) = default;
154 // Us and the coroutine frame; but we're about to be destroyed
155 if (core_.use_count() == 2) {
156 // -> cancel generator. Because the coroutine frame keeps a reference to
157 // the core, we can't do this in the core's destructor.
158 PromiseCore_ *weakCore = core_.get();
159 core_.reset();
160 // The core's dtor is called while cancelGenerator runs.
161 weakCore->cancelGenerator();
162 }
163 }
164
167 Promise<std::optional<T>> next() { co_return (co_await *this); }
168
174 MultiPromiseAwaiter_ operator co_await() const {
175 BOOST_ASSERT(core_);
177 }
178
181 bool ready() { return core_->slot.has_value() || core_->isTerminated(); }
182
197 void cancel() { core_->cancelGenerator(); }
198
199protected:
204 constexpr explicit MultiPromiseAwaiter_(SharedCore_ core)
205 : core_{std::move(core)} {}
211
214 [[nodiscard]] bool await_ready() const {
215 return core_->isTerminated() || core_->slot.has_value();
216 }
219 [[nodiscard]] bool await_suspend(std::coroutine_handle<> handle) const {
220 BOOST_ASSERT_MSG(!core_->willResume(),
221 "promise is already being waited on!\n");
222 core_->setHandle(handle);
223 core_->resumeGenerator();
224 return true;
225 }
232 std::optional<T> await_resume() const {
233 if (!core_->slot) {
234 // Terminated by co_return
235 return std::nullopt;
236 }
237 switch (core_->slot->index()) {
238 [[likely]] case 0: {
239 std::optional<T> result = std::move(std::get<0>(core_->slot.value()));
240 core_->slot.reset();
241 return std::move(result);
242 }
243 case 1:
244 // Terminated by exception
245 BOOST_ASSERT(core_->isTerminated());
246 std::rethrow_exception(std::get<1>(core_->slot.value()));
247 default:
248 throw UvcoException("MultiPromiseAwaiter_::await_resume: invalid slot");
249 }
250 }
251
253 };
254
255 template <typename U> friend class Generator;
256
257 explicit MultiPromise(SharedCore_ core) : core_{std::move(core)} {}
258
260};
261
264template <typename T> class Generator {
265 struct YieldAwaiter_;
267 using SharedCore_ = std::shared_ptr<PromiseCore_>;
268
269public:
270 // A generator promise object is pinned in memory (copy/move forbidden).
271 Generator() : core_{std::make_shared<PromiseCore_>()} {}
272 Generator(Generator<T> &&) noexcept = delete;
273 Generator &operator=(const Generator<T> &) = delete;
274 Generator &operator=(Generator<T> &&) noexcept = delete;
275 Generator(const Generator<T> &other) = delete;
276 ~Generator() = default;
277
280
283 void return_void() {
284 core_->terminated();
285 core_->resume();
286 }
287
289 // Note: if suspend_always is chosen, we can better control when the
290 // MultiPromise will be scheduled.
291 std::suspend_never initial_suspend() noexcept { return {}; }
293 std::suspend_never final_suspend() noexcept { return {}; }
294
297 core_->slot = std::current_exception();
298 core_->terminated();
299 core_->resume();
300 }
301
313 BOOST_ASSERT(!core_->slot);
314 core_->slot = std::move(value);
315 core_->resume();
316 return YieldAwaiter_{*core_};
317 }
318
319private:
324 explicit YieldAwaiter_(PromiseCore_ &core) : core_{core} {}
325
326 [[nodiscard]] bool await_ready() const { return !core_.slot.has_value(); }
327
328 bool await_suspend(std::coroutine_handle<> handle) {
329 core_.suspendGenerator(handle);
330 return true;
331 }
332
334 // Returning into the generator coroutine
335 }
336
338 };
339
341};
342
344
345} // namespace uvco
Definition multipromise.h:264
Generator(Generator< T > &&) noexcept=delete
void unhandled_exception()
Part of the coroutine protocol (see Promise).
Definition multipromise.h:296
SharedCore_ core_
Definition multipromise.h:340
Generator()
Definition multipromise.h:271
MultiPromise< T > get_return_object()
A generator (yielding) coroutine returns a MultiPromise.
Definition multipromise.h:279
std::suspend_never final_suspend() noexcept
Part of the coroutine protocol (see Promise).
Definition multipromise.h:293
std::shared_ptr< PromiseCore_ > SharedCore_
Definition multipromise.h:267
void return_void()
Definition multipromise.h:283
YieldAwaiter_ yield_value(T value)
Definition multipromise.h:312
std::suspend_never initial_suspend() noexcept
Part of the coroutine protocol (see Promise).
Definition multipromise.h:291
static void enqueue(std::coroutine_handle<> handle)
Definition loop.cc:73
Definition multipromise.h:23
MultiPromiseCore(MultiPromiseCore &&)=delete
MultiPromiseCore(const MultiPromiseCore &)=delete
void terminated()
Definition multipromise.h:106
void cancelGenerator()
Definition multipromise.h:92
void resume() override
Definition multipromise.h:59
void setHandle(std::coroutine_handle<> handle) override
Definition multipromise.h:39
MultiPromiseCore & operator=(MultiPromiseCore &&)=delete
std::optional< std::coroutine_handle<> > generatorHandle_
Coroutine handle referring to the suspended generator.
Definition multipromise.h:113
bool isTerminated() const
Check if the generator has been cancelled or has returned.
Definition multipromise.h:109
void suspendGenerator(std::coroutine_handle<> handle)
Definition multipromise.h:80
~MultiPromiseCore() override=default
bool terminated_
Definition multipromise.h:116
MultiPromiseCore & operator=(const MultiPromiseCore &)=delete
void resumeGenerator()
Definition multipromise.h:66
Definition multipromise.h:133
Promise< std::optional< T > > next()
Definition multipromise.h:167
std::shared_ptr< PromiseCore_ > SharedCore_
Definition multipromise.h:138
MultiPromise(MultiPromise< T > &&) noexcept=default
An unfulfilled MultiPromise.
MultiPromise(SharedCore_ core)
Definition multipromise.h:257
SharedCore_ core_
Definition multipromise.h:259
void cancel()
Definition multipromise.h:197
bool ready()
Definition multipromise.h:181
Definition promise_core.h:61
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:76
Definition async_work.cc:17
Definition multipromise.h:323
bool await_suspend(std::coroutine_handle<> handle)
Definition multipromise.h:328
PromiseCore_ & core_
Definition multipromise.h:337
void await_resume()
Definition multipromise.h:333
YieldAwaiter_(PromiseCore_ &core)
Definition multipromise.h:324
bool await_ready() const
Definition multipromise.h:326
Definition multipromise.h:203
SharedCore_ core_
Definition multipromise.h:252
MultiPromiseAwaiter_ & operator=(const MultiPromiseAwaiter_ &)=delete
MultiPromiseAwaiter_(MultiPromiseAwaiter_ &&)=delete
std::optional< T > await_resume() const
Definition multipromise.h:232
MultiPromiseAwaiter_ & operator=(MultiPromiseAwaiter_ &&)=delete
MultiPromiseAwaiter_(const MultiPromiseAwaiter_ &)=delete
bool await_suspend(std::coroutine_handle<> handle) const
Definition multipromise.h:219
constexpr MultiPromiseAwaiter_(SharedCore_ core)
Definition multipromise.h:204
bool await_ready() const
Definition multipromise.h:214
Definition exception.h:19