COROIO: coroio/corochain.hpp Source File
COROIO
 
Loading...
Searching...
No Matches
corochain.hpp
Go to the documentation of this file.
1#pragma once
2
57#include <coroutine>
58#include <optional>
59#include <expected>
60#include <memory>
61#include <functional>
62#include <exception>
63#include <utility>
64
65#include "promises.hpp"
66#include "poller.hpp"
67
68namespace NNet {
69
70template<typename T> struct TFinalAwaiter;
71
72template<typename T> struct TFuture;
73
82template<typename T>
84 std::suspend_never initial_suspend() { return {}; }
85 TFinalAwaiter<T> final_suspend() noexcept;
87 std::coroutine_handle<> Caller = std::noop_coroutine();
88};
89
98template<typename T>
99struct TPromise: public TPromiseBase<T> {
100 TFuture<T> get_return_object();
101
102 void return_value(const T& t) {
103 ErrorOr = t;
104 }
105
106 void return_value(T&& t) {
107 ErrorOr = std::move(t);
108 }
109
110 void unhandled_exception() {
111 ErrorOr = std::unexpected(std::current_exception());
112 }
113
115 std::optional<std::expected<T, std::exception_ptr>> ErrorOr;
116};
117
126template<typename T>
128 TFutureBase() = default;
129 TFutureBase(TPromise<T>& promise)
130 : Coro(Coro.from_promise(promise))
131 { }
132 TFutureBase(TFutureBase&& other)
133 {
134 *this = std::move(other);
135 }
136 TFutureBase(const TFutureBase&) = delete;
137 TFutureBase& operator=(const TFutureBase&) = delete;
138 TFutureBase& operator=(TFutureBase&& other) {
139 if (this != &other) {
140 if (Coro) {
141 Coro.destroy();
142 }
143 Coro = std::exchange(other.Coro, nullptr);
144 }
145 return *this;
146 }
147
148 ~TFutureBase() { if (Coro) { Coro.destroy(); } }
149
150 bool await_ready() const {
151 return Coro.promise().ErrorOr.has_value();
152 }
153
154 bool done() const {
155 return Coro.done();
156 }
157
158 std::coroutine_handle<> raw() {
159 return Coro;
160 }
161
162 void await_suspend(std::coroutine_handle<> caller) {
163 Coro.promise().Caller = caller;
164 }
165
167
168protected:
169 std::coroutine_handle<TPromise<T>> Coro = nullptr;
170};
171
172template<> struct TFuture<void>;
173
184template<typename T>
185struct TFuture : public TFutureBase<T> {
186 T await_resume() {
187 auto& errorOr = *this->Coro.promise().ErrorOr;
188 if (errorOr.has_value()) {
189 return std::move(errorOr.value());
190 } else {
191 std::rethrow_exception(errorOr.error());
192 }
193 }
194
195
206 template<typename Func>
208 auto prev = std::move(*this);
209 auto ret = co_await prev;
210 co_return func(ret);
211 }
212
221};
222
226template<>
227struct TPromise<void>: public TPromiseBase<void> {
228 TFuture<void> get_return_object();
229
230 void return_void() {
231 ErrorOr = nullptr;
232 }
233
234 void unhandled_exception() {
235 ErrorOr = std::current_exception();
236 }
237
238 std::optional<std::exception_ptr> ErrorOr;
239};
240
248template<>
249struct TFuture<void> : public TFutureBase<void> {
250 void await_resume() {
251 auto& errorOr = *this->Coro.promise().ErrorOr;
252 if (errorOr) {
253 std::rethrow_exception(errorOr);
254 }
255 }
256
264 template<typename Func>
265 auto Accept(Func func) -> TFuture<void> {
266 auto prev = std::move(*this);
267 co_await prev;
268 co_return func();
269 }
270};
271
272template<typename T>
274 auto prev = std::move(*this);
275 co_await prev;
276 co_return;
277}
278
289template<typename T>
291 bool await_ready() noexcept { return false; }
292 std::coroutine_handle<> await_suspend(std::coroutine_handle<TPromise<T>> h) noexcept {
293 return h.promise().Caller;
294 }
295 void await_resume() noexcept { }
296};
297
299template<typename T>
300TFuture<T> TPromise<T>::get_return_object() { return { TFuture<T>{*this} }; }
301
302
303template<typename T>
304TFinalAwaiter<T> TPromiseBase<T>::final_suspend() noexcept { return {}; }
305
316template<typename T>
317TFuture<std::vector<T>> All(std::vector<TFuture<T>>&& futures) {
318 auto waiting = std::move(futures);
319 std::vector<T> ret; ret.reserve(waiting.size());
320 for (auto& f : waiting) {
321 ret.emplace_back(std::move(co_await f));
322 }
323 co_return ret;
324}
325
332inline TFuture<void> All(std::vector<TFuture<void>>&& futures) {
333 auto waiting = std::move(futures);
334 for (auto& f : waiting) {
335 co_await f;
336 }
337 co_return;
338}
339
352template<typename T>
353TFuture<T> Any(std::vector<TFuture<T>>&& futures) {
354 std::vector<TFuture<T>> all = std::move(futures);
355
356 auto it = std::find_if(all.begin(), all.end(), [](auto& f) { return f.done(); });
357 if (it != all.end()) {
358 co_return it->await_resume();
359 }
360
361 auto self = co_await Self();
362 for (auto& f : all) {
363 f.await_suspend(self);
364 }
365 co_await std::suspend_always();
366 co_return std::find_if(all.begin(), all.end(), [](auto& f) { return f.done(); })->await_resume();
367}
368
375inline TFuture<void> Any(std::vector<TFuture<void>>&& futures) {
376 std::vector<TFuture<void>> all = std::move(futures);
377
378 if (std::find_if(all.begin(), all.end(), [](auto& f) { return f.done(); }) != all.end()) {
379 co_return;
380 }
381
382 auto self = co_await Self();
383 for (auto& f : all) {
384 f.await_suspend(self);
385 }
386 co_await std::suspend_always();
387 co_return;
388}
389
390} // namespace NNet
TFuture< T > Any(std::vector< TFuture< T > > &&futures)
Returns the result of whichever future completes first.
Definition corochain.hpp:353
TFuture< std::vector< T > > All(std::vector< TFuture< T > > &&futures)
Awaits every future in order and collects their results.
Definition corochain.hpp:317
Final awaiter for a coroutine.
Definition corochain.hpp:290
Base future type for coroutines.
Definition corochain.hpp:127
auto Accept(Func func) -> TFuture< void >
Registers a continuation to be executed after the coroutine completes.
Definition corochain.hpp:265
Owned coroutine handle that carries a result of type T.
Definition corochain.hpp:185
auto Apply(Func func) -> TFuture< decltype(func(std::declval< T >()))>
Applies a function to the result of the coroutine.
Definition corochain.hpp:207
TFuture< void > Ignore()
Awaits the coroutine and ignores its result.
Definition corochain.hpp:273
Base promise type for coroutines.
Definition corochain.hpp:83
std::coroutine_handle Caller
Handle to the caller coroutine (initialized to a no-operation coroutine).
Definition corochain.hpp:87
Promise for coroutines that return a value of type T.
Definition corochain.hpp:99
std::optional< std::expected< T, std::exception_ptr > > ErrorOr
Optional container that holds either the result or an exception.
Definition corochain.hpp:115