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
181template<typename T>
182struct TFuture : public TFutureBase<T> {
183 T await_resume() {
184 auto& errorOr = *this->Coro.promise().ErrorOr;
185 if (errorOr.has_value()) {
186 return std::move(errorOr.value());
187 } else {
188 std::rethrow_exception(errorOr.error());
189 }
190 }
191
192
203 template<typename Func>
205 auto prev = std::move(*this);
206 auto ret = co_await prev;
207 co_return func(ret);
208 }
209
218};
219
223template<>
224struct TPromise<void>: public TPromiseBase<void> {
225 TFuture<void> get_return_object();
226
227 void return_void() {
228 ErrorOr = nullptr;
229 }
230
231 void unhandled_exception() {
232 ErrorOr = std::current_exception();
233 }
234
235 std::optional<std::exception_ptr> ErrorOr;
236};
237
241template<>
242struct TFuture<void> : public TFutureBase<void> {
243 void await_resume() {
244 auto& errorOr = *this->Coro.promise().ErrorOr;
245 if (errorOr) {
246 std::rethrow_exception(errorOr);
247 }
248 }
249
257 template<typename Func>
258 auto Accept(Func func) -> TFuture<void> {
259 auto prev = std::move(*this);
260 co_await prev;
261 co_return func();
262 }
263};
264
265template<typename T>
267 auto prev = std::move(*this);
268 co_await prev;
269 co_return;
270}
271
282template<typename T>
284 bool await_ready() noexcept { return false; }
285 std::coroutine_handle<> await_suspend(std::coroutine_handle<TPromise<T>> h) noexcept {
286 return h.promise().Caller;
287 }
288 void await_resume() noexcept { }
289};
290
292template<typename T>
293TFuture<T> TPromise<T>::get_return_object() { return { TFuture<T>{*this} }; }
294
295
296template<typename T>
297TFinalAwaiter<T> TPromiseBase<T>::final_suspend() noexcept { return {}; }
298
306template<typename T>
307TFuture<std::vector<T>> All(std::vector<TFuture<T>>&& futures) {
308 auto waiting = std::move(futures);
309 std::vector<T> ret; ret.reserve(waiting.size());
310 for (auto& f : waiting) {
311 ret.emplace_back(std::move(co_await f));
312 }
313 co_return ret;
314}
315
322inline TFuture<void> All(std::vector<TFuture<void>>&& futures) {
323 auto waiting = std::move(futures);
324 for (auto& f : waiting) {
325 co_await f;
326 }
327 co_return;
328}
329
340template<typename T>
341TFuture<T> Any(std::vector<TFuture<T>>&& futures) {
342 std::vector<TFuture<T>> all = std::move(futures);
343
344 auto it = std::find_if(all.begin(), all.end(), [](auto& f) { return f.done(); });
345 if (it != all.end()) {
346 co_return it->await_resume();
347 }
348
349 auto self = co_await Self();
350 for (auto& f : all) {
351 f.await_suspend(self);
352 }
353 co_await std::suspend_always();
354 co_return std::find_if(all.begin(), all.end(), [](auto& f) { return f.done(); })->await_resume();
355}
356
363inline TFuture<void> Any(std::vector<TFuture<void>>&& futures) {
364 std::vector<TFuture<void>> all = std::move(futures);
365
366 if (std::find_if(all.begin(), all.end(), [](auto& f) { return f.done(); }) != all.end()) {
367 co_return;
368 }
369
370 auto self = co_await Self();
371 for (auto& f : all) {
372 f.await_suspend(self);
373 }
374 co_await std::suspend_always();
375 co_return;
376}
377
378} // 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:341
TFuture< std::vector< T > > All(std::vector< TFuture< T > > &&futures)
Awaits the completion of all futures and collects their results.
Definition corochain.hpp:307
Final awaiter for a coroutine.
Definition corochain.hpp:283
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:258
Future type for coroutines returning a value of type T.
Definition corochain.hpp:182
auto Apply(Func func) -> TFuture< decltype(func(std::declval< T >()))>
Applies a function to the result of the coroutine.
Definition corochain.hpp:204
TFuture< void > Ignore()
Awaits the coroutine and ignores its result.
Definition corochain.hpp:266
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