COROIO: coroio/corochain.hpp Source File
COROIO
 
Loading...
Searching...
No Matches
corochain.hpp
Go to the documentation of this file.
1#pragma once
2
56
57#include <coroutine>
58#include <optional>
59#include <variant>
60#include <memory>
61#include <functional>
62
63#include "promises.hpp"
64#include "poller.hpp"
65
66namespace NNet {
67
68template<typename T> struct TFinalAwaiter;
69
70template<typename T> struct TFuture;
71
80template<typename T>
82 std::suspend_never initial_suspend() { return {}; }
83 TFinalAwaiter<T> final_suspend() noexcept;
85 std::coroutine_handle<> Caller = std::noop_coroutine();
86};
87
96template<typename T>
97struct TPromise: public TPromiseBase<T> {
98 TFuture<T> get_return_object();
99
100 void return_value(const T& t) {
101 ErrorOr = t;
102 }
103
104 void return_value(T&& t) {
105 ErrorOr = std::move(t);
106 }
107
108 void unhandled_exception() {
109 ErrorOr = std::current_exception();
110 }
111
113 std::optional<std::variant<T, std::exception_ptr>> ErrorOr;
114};
115
124template<typename T>
125struct TFutureBase {
126 TFutureBase() = default;
127 TFutureBase(TPromise<T>& promise)
128 : Coro(Coro.from_promise(promise))
129 { }
130 TFutureBase(TFutureBase&& other)
131 {
132 *this = std::move(other);
133 }
134 TFutureBase(const TFutureBase&) = delete;
135 TFutureBase& operator=(const TFutureBase&) = delete;
136 TFutureBase& operator=(TFutureBase&& other) {
137 if (this != &other) {
138 std::swap(Coro, other.Coro);
139 }
140 return *this;
141 }
142
143 ~TFutureBase() { if (Coro) { Coro.destroy(); } }
144
145 bool await_ready() const {
146 return Coro.promise().ErrorOr != std::nullopt;
147 }
148
149 bool done() const {
150 return Coro.done();
151 }
152
153 std::coroutine_handle<> raw() {
154 return Coro;
155 }
156
157 void await_suspend(std::coroutine_handle<> caller) {
158 Coro.promise().Caller = caller;
159 }
160
161 using promise_type = TPromise<T>;
162
163protected:
164 std::coroutine_handle<TPromise<T>> Coro = nullptr;
165};
166
167template<> struct TFuture<void>;
168
176template<typename T>
177struct TFuture : public TFutureBase<T> {
178 T await_resume() {
179 auto& errorOr = *this->Coro.promise().ErrorOr;
180 if (auto* res = std::get_if<T>(&errorOr)) {
181 return std::move(*res);
182 } else {
183 std::rethrow_exception(std::get<std::exception_ptr>(errorOr));
184 }
185 }
186
187
198 template<typename Func>
200 auto prev = std::move(*this);
201 auto ret = co_await prev;
202 co_return func(ret);
203 }
204
213};
214
218template<>
219struct TPromise<void>: public TPromiseBase<void> {
220 TFuture<void> get_return_object();
221
222 void return_void() {
223 ErrorOr = nullptr;
224 }
225
226 void unhandled_exception() {
227 ErrorOr = std::current_exception();
228 }
229
230 std::optional<std::exception_ptr> ErrorOr;
231};
232
236template<>
237struct TFuture<void> : public TFutureBase<void> {
238 void await_resume() {
239 auto& errorOr = *this->Coro.promise().ErrorOr;
240 if (errorOr) {
241 std::rethrow_exception(errorOr);
242 }
243 }
244
252 template<typename Func>
253 auto Accept(Func func) -> TFuture<void> {
254 auto prev = std::move(*this);
255 co_await prev;
256 co_return func();
257 }
258};
259
260template<typename T>
262 auto prev = std::move(*this);
263 co_await prev;
264 co_return;
265}
266
277template<typename T>
279 bool await_ready() noexcept { return false; }
280 std::coroutine_handle<> await_suspend(std::coroutine_handle<TPromise<T>> h) noexcept {
281 return h.promise().Caller;
282 }
283 void await_resume() noexcept { }
284};
285
286inline TFuture<void> TPromise<void>::get_return_object() { return { TFuture<void>{*this} }; }
287template<typename T>
288TFuture<T> TPromise<T>::get_return_object() { return { TFuture<T>{*this} }; }
289
290
291template<typename T>
292TFinalAwaiter<T> TPromiseBase<T>::final_suspend() noexcept { return {}; }
293
301template<typename T>
302TFuture<std::vector<T>> All(std::vector<TFuture<T>>&& futures) {
303 auto waiting = std::move(futures);
304 std::vector<T> ret; ret.reserve(waiting.size());
305 for (auto& f : waiting) {
306 ret.emplace_back(std::move(co_await f));
307 }
308 co_return ret;
309}
310
317inline TFuture<void> All(std::vector<TFuture<void>>&& futures) {
318 auto waiting = std::move(futures);
319 for (auto& f : waiting) {
320 co_await f;
321 }
322 co_return;
323}
324
335template<typename T>
336TFuture<T> Any(std::vector<TFuture<T>>&& futures) {
337 std::vector<TFuture<T>> all = std::move(futures);
338
339 auto it = std::find_if(all.begin(), all.end(), [](auto& f) { return f.done(); });
340 if (it != all.end()) {
341 co_return it->await_resume();
342 }
343
344 auto self = co_await Self();
345 for (auto& f : all) {
346 f.await_suspend(self);
347 }
348 co_await std::suspend_always();
349 co_return std::find_if(all.begin(), all.end(), [](auto& f) { return f.done(); })->await_resume();
350}
351
358inline TFuture<void> Any(std::vector<TFuture<void>>&& futures) {
359 std::vector<TFuture<void>> all = std::move(futures);
360
361 if (std::find_if(all.begin(), all.end(), [](auto& f) { return f.done(); }) != all.end()) {
362 co_return;
363 }
364
365 auto self = co_await Self();
366 for (auto& f : all) {
367 f.await_suspend(self);
368 }
369 co_await std::suspend_always();
370 co_return;
371}
372
373} // namespace NNet
A minimal example of a coroutine "awaitable" object.
TFuture< T > Any(std::vector< TFuture< T > > &&futures)
Awaits the completion of any one of the given futures and returns its result.
Definition corochain.hpp:336
TFuture< std::vector< T > > All(std::vector< TFuture< T > > &&futures)
Awaits the completion of all futures and collects their results.
Definition corochain.hpp:302
Final awaiter for a coroutine.
Definition corochain.hpp:278
auto Accept(Func func) -> TFuture< void >
Registers a continuation to be executed after the coroutine completes.
Definition corochain.hpp:253
Future type for coroutines returning a value of type T.
Definition corochain.hpp:177
auto Apply(Func func) -> TFuture< decltype(func(std::declval< T >()))>
Applies a function to the result of the coroutine.
Definition corochain.hpp:199
TFuture< void > Ignore()
Awaits the coroutine and ignores its result.
Definition corochain.hpp:261
Base promise type for coroutines.
Definition corochain.hpp:81
std::coroutine_handle Caller
Handle to the caller coroutine (initialized to a no-operation coroutine).
Definition corochain.hpp:85
Promise for coroutines that return a value of type T.
Definition corochain.hpp:97
std::optional< std::variant< T, std::exception_ptr > > ErrorOr
Optional container that holds either the result or an exception.
Definition corochain.hpp:113