cortex 0.0.1
Loading...
Searching...
No Matches
future.hpp
Go to the documentation of this file.
1#pragma once
2
5
6#include <cassert>
7#include <optional>
8#include <stdexcept>
9#include <type_traits>
10#include <utility>
11
17namespace cortex::tiny_fiber {
18
19namespace detail {
20
21// Shared state for a fiber's result (internal).
22template <typename T>
25 std::optional<T> result;
26 std::exception_ptr exception;
27 bool retrieved {false};
28};
29
30template <>
31struct FutureState<void> {
33 std::exception_ptr exception;
34 bool retrieved {false};
35};
36
37} // namespace detail
38
45template <typename T>
46class Future {
47public:
48 Future(const Future&) = delete;
49 Future& operator=(const Future&) = delete;
50
51 Future(Future&& other) noexcept
52 : state_(std::move(other.state_))
53 , scheduler_(other.scheduler_) {
54 other.scheduler_ = nullptr;
55 }
56
57 Future& operator=(Future&& other) noexcept {
58 if (this != &other) {
59 // Wait for current fiber if needed
60 if (state_ && scheduler_ && !state_->retrieved) {
61 WaitImpl();
62 }
63 state_ = std::move(other.state_);
64 scheduler_ = other.scheduler_;
65 other.scheduler_ = nullptr;
66 }
67 return *this;
68 }
69
71 // Destructor waits for fiber completion
72 if (state_ && scheduler_ && !state_->retrieved) {
73 try {
74 WaitImpl();
75 } catch (...) {
76 // Suppress exceptions in destructor
77 }
78 }
79 }
80
84 void Wait() {
85 if (!state_) {
86 throw std::logic_error("Future has no state");
87 }
88 WaitImpl();
89 }
90
97 T Get() {
98 if (!state_) {
99 throw std::logic_error("Future has no state");
100 }
101 if (state_->retrieved) {
102 throw std::logic_error("Future result already retrieved");
103 }
104
105 WaitImpl();
106 state_->retrieved = true;
107
108 // Check for exception (stored in state for safety after fiber cleanup)
109 if (state_->exception) {
110 std::rethrow_exception(state_->exception);
111 }
112
113 if (!state_->result.has_value()) {
114 throw std::logic_error("Fiber completed without result");
115 }
116
117 return std::move(*state_->result);
118 }
119
123 [[nodiscard]] bool IsReady() const noexcept {
124 if (!state_ || !scheduler_) {
125 return true;
126 }
127 auto* fiber = scheduler_->GetFiber(state_->fiber_id);
128 return !fiber || fiber->IsDone();
129 }
130
131private:
132 template <typename U>
133 friend Future<U> Spawn(std::function<U()> func);
134
135 template <typename U>
136 friend Future<U> Spawn(std::function<U()> func, std::size_t stack_size);
137
138 template <typename F>
139 friend auto Spawn(F&& func) -> Future<std::invoke_result_t<F>>;
140
141 template <typename F>
142 friend auto Spawn(F&& func, std::size_t stack_size) -> Future<std::invoke_result_t<F>>;
143
144 Future(std::shared_ptr<detail::FutureState<T>> state, Scheduler* scheduler)
145 : state_(std::move(state))
146 , scheduler_(scheduler) {}
147
148 void WaitImpl() {
149 assert(state_);
150 assert(scheduler_);
151
152 auto* fiber = scheduler_->GetFiber(state_->fiber_id);
153 if (!fiber || fiber->IsDone()) {
154 // Fiber already done or cleaned up, exception already in state
155 return;
156 }
157
158 // Add current fiber as waiter
159 auto* current = scheduler_->GetCurrentFiber();
160 if (current) {
161 fiber->AddWaiter(current);
162 scheduler_->SuspendCurrent();
163 }
164 // Exception is stored in state by Spawn wrapper, no need to copy here
165 }
166
167 std::shared_ptr<detail::FutureState<T>> state_;
168 Scheduler* scheduler_ {nullptr};
169};
170
174template <>
175class Future<void> {
176public:
177 Future(const Future&) = delete;
178 Future& operator=(const Future&) = delete;
179
180 Future(Future&& other) noexcept
181 : state_(std::move(other.state_))
182 , scheduler_(other.scheduler_) {
183 other.scheduler_ = nullptr;
184 }
185
186 Future& operator=(Future&& other) noexcept {
187 if (this != &other) {
188 if (state_ && scheduler_ && !state_->retrieved) {
189 WaitImpl();
190 }
191 state_ = std::move(other.state_);
192 scheduler_ = other.scheduler_;
193 other.scheduler_ = nullptr;
194 }
195 return *this;
196 }
197
199 if (state_ && scheduler_ && !state_->retrieved) {
200 try {
201 WaitImpl();
202 } catch (...) {
203 // Suppress exceptions in destructor
204 }
205 }
206 }
207
213 void Wait() {
214 if (!state_) {
215 throw std::logic_error("Future has no state");
216 }
217 WaitImpl();
218 state_->retrieved = true;
219
220 // Check for exception (stored in state for safety after fiber cleanup)
221 if (state_->exception) {
222 std::rethrow_exception(state_->exception);
223 }
224 }
225
229 [[nodiscard]] bool IsReady() const noexcept {
230 if (!state_ || !scheduler_) {
231 return true;
232 }
233 auto* fiber = scheduler_->GetFiber(state_->fiber_id);
234 return !fiber || fiber->IsDone();
235 }
236
237private:
238 template <typename U>
239 friend Future<U> Spawn(std::function<U()> func);
240
241 template <typename U>
242 friend Future<U> Spawn(std::function<U()> func, std::size_t stack_size);
243
244 template <typename F>
245 friend auto Spawn(F&& func) -> Future<std::invoke_result_t<F>>;
246
247 template <typename F>
248 friend auto Spawn(F&& func, std::size_t stack_size) -> Future<std::invoke_result_t<F>>;
249
250 Future(std::shared_ptr<detail::FutureState<void>> state, Scheduler* scheduler)
251 : state_(std::move(state))
252 , scheduler_(scheduler) {}
253
254 void WaitImpl() {
255 assert(state_);
256 assert(scheduler_);
257
258 auto* fiber = scheduler_->GetFiber(state_->fiber_id);
259 if (!fiber || fiber->IsDone()) {
260 // Fiber already done or cleaned up, exception already in state
261 return;
262 }
263
264 auto* current = scheduler_->GetCurrentFiber();
265 if (current) {
266 fiber->AddWaiter(current);
267 scheduler_->SuspendCurrent();
268 }
269 // Exception is stored in state by Spawn wrapper, no need to copy here
270 }
271
272 std::shared_ptr<detail::FutureState<void>> state_;
273 Scheduler* scheduler_ {nullptr};
274};
275
283template <typename F>
284auto Spawn(F&& func, std::size_t stack_size) -> Future<std::invoke_result_t<F>> {
285 using ResultType = std::invoke_result_t<F>;
286
287 auto& scheduler = Scheduler::Current();
288 auto state = std::make_shared<detail::FutureState<ResultType>>();
289
290 if constexpr (std::is_void_v<ResultType>) {
291 auto fiber_id = scheduler.SpawnFiberInternal(
292 [state, f = std::forward<F>(func)]() mutable {
293 try {
294 f();
295 } catch (...) {
296 // Store exception in state immediately (fiber may be cleaned up later)
297 state->exception = std::current_exception();
298 throw; // Re-throw so scheduler knows fiber failed
299 }
300 },
301 stack_size);
302 state->fiber_id = fiber_id;
303 } else {
304 auto fiber_id = scheduler.SpawnFiberInternal(
305 [state, f = std::forward<F>(func)]() mutable {
306 try {
307 state->result = f();
308 } catch (...) {
309 // Store exception in state immediately (fiber may be cleaned up later)
310 state->exception = std::current_exception();
311 throw;
312 }
313 },
314 stack_size);
315 state->fiber_id = fiber_id;
316 }
317
318 return Future<ResultType>(std::move(state), &scheduler);
319}
320
327template <typename F>
329 return Spawn(std::forward<F>(func), Scheduler::Current().GetDefaultStackSize());
330}
331
332} // namespace cortex::tiny_fiber
bool IsDone() const noexcept
Checks if the coroutine has finished its execution.
Definition base_coroutine.hpp:37
Future(const Future &)=delete
~Future()
Definition future.hpp:198
bool IsReady() const noexcept
Check if the fiber has completed.
Definition future.hpp:229
Future(Future &&other) noexcept
Definition future.hpp:180
friend Future< U > Spawn(std::function< U()> func)
Future & operator=(const Future &)=delete
void Wait()
Block current fiber until this fiber completes.
Definition future.hpp:213
friend Future< U > Spawn(std::function< U()> func, std::size_t stack_size)
Future & operator=(Future &&other) noexcept
Definition future.hpp:186
Handle to a spawned fiber that returns a value.
Definition future.hpp:46
~Future()
Definition future.hpp:70
T Get()
Block until fiber completes and return the result.
Definition future.hpp:97
Future & operator=(Future &&other) noexcept
Definition future.hpp:57
friend Future< U > Spawn(std::function< U()> func)
Future & operator=(const Future &)=delete
bool IsReady() const noexcept
Check if the fiber has completed.
Definition future.hpp:123
Future(const Future &)=delete
friend Future< U > Spawn(std::function< U()> func, std::size_t stack_size)
void Wait()
Block current fiber until this fiber completes.
Definition future.hpp:84
Future(Future &&other) noexcept
Definition future.hpp:51
Manages cooperative execution of fibers.
Definition scheduler.hpp:28
static Scheduler & Current()
Get the current scheduler.
std::uint64_t Id
Definition fiber.hpp:34
Cooperative multitasking primitives built on cortex::Coroutine.
Definition condition_variable.hpp:13
auto Spawn(F &&func, std::size_t stack_size) -> Future< std::invoke_result_t< F > >
Spawn a new fiber with custom stack size.
Definition future.hpp:284
Cooperative fiber scheduler for tiny_fiber.
std::exception_ptr exception
Definition future.hpp:33
Fiber::Id fiber_id
Definition future.hpp:24
std::optional< T > result
Definition future.hpp:25
std::exception_ptr exception
Definition future.hpp:26
bool retrieved
Definition future.hpp:27