A11 (C++ runtime)
Native C++ implementation of the A11 action and streaming runtime
Loading...
Searching...
No Matches
future.h
Go to the documentation of this file.
1// Copyright 2026 The A11 Authors.
2
13#ifndef A11_CONCURRENCY_FUTURE_H_
14#define A11_CONCURRENCY_FUTURE_H_
15
16#include <exception>
17#include <functional>
18#include <memory>
19#include <optional>
20#include <type_traits>
21#include <utility>
22#include <vector>
23
24#include <absl/base/nullability.h>
25#include <absl/functional/any_invocable.h>
26#include <absl/log/log.h>
27#include <absl/status/status.h>
28#include <absl/status/statusor.h>
29#include <absl/time/clock.h>
30#include <absl/time/time.h>
31
32#include "thread/boost_primitives.h"
33#include "thread/fiber.h"
34#include "thread/select.h"
35#include "thread/selectables.h"
36
37namespace a11 {
38
40struct Unit {
41 friend bool operator==(Unit, Unit) = default;
42};
43
44template <typename T>
45class Future;
46
47template <typename T>
48class Promise;
49
50namespace internal {
51
52template <typename T>
53struct FutureState {
54 mutable thread::Mutex mu;
55 thread::CondVar cv;
56 bool ready ABSL_GUARDED_BY(mu) = false;
57 std::optional<absl::StatusOr<T>> result ABSL_GUARDED_BY(mu);
58 std::function<void()> cancel ABSL_GUARDED_BY(mu);
59 thread::PermanentEvent event;
60 std::vector<absl::AnyInvocable<void(const absl::StatusOr<T>&)>> callbacks
61 ABSL_GUARDED_BY(mu);
62};
63
64template <typename T>
66 absl::AnyInvocable<void(const absl::StatusOr<T>&)> callback,
67 const absl::StatusOr<T>& result) {
68 try {
69 callback(result);
70 } catch (const std::exception& error) {
71 LOG(ERROR) << "Future completion callback raised: " << error.what();
72 } catch (...) {
73 LOG(ERROR) << "Future completion callback raised a non-standard exception";
74 }
75}
76
77} // namespace internal
78
88template <typename T>
90 absl::AnyInvocable<absl::StatusOr<T>() &&> work,
91 std::function<void()> cancellation_hook,
92 thread::TreeOptions tree_options = {});
93
94template <typename T>
95Future<T> Submit(absl::AnyInvocable<absl::StatusOr<T>() &&> work,
96 thread::TreeOptions tree_options = {});
97
109template <typename T>
110class Future {
111 public:
112 Future() = default;
113
115 [[nodiscard]] bool valid() const { return state_ != nullptr; }
116
118 [[nodiscard]] bool IsReady() const {
119 if (state_ == nullptr) {
120 return false;
121 }
122 thread::MutexLock lock(&state_->mu);
123 return state_->ready;
124 }
125
131 absl::Status Cancel() const {
132 if (state_ == nullptr) {
133 return absl::FailedPreconditionError("Future is not valid");
134 }
135 std::function<void()> cancel;
136 {
137 thread::MutexLock lock(&state_->mu);
138 if (state_->ready) {
139 return absl::OkStatus();
140 }
141 cancel = state_->cancel;
142 }
143
144 if (cancel == nullptr) {
145 return absl::UnimplementedError(
146 "This Future does not have a cancellation source");
147 }
148
149 try {
150 cancel();
151 return absl::OkStatus();
152 } catch (const std::exception& error) {
153 return absl::UnknownError(error.what());
154 } catch (...) {
155 return absl::UnknownError(
156 "Future cancellation raised a non-standard exception");
157 }
158 }
159
166 absl::StatusOr<T> Await(absl::Time deadline = absl::InfiniteFuture()) const {
167 if (state_ == nullptr) {
168 return absl::FailedPreconditionError("Future is not valid");
169 }
170 {
171 thread::MutexLock lock(&state_->mu);
172 if (state_->ready) {
173 return *state_->result;
174 }
175 }
176
177 // A dynamic A11 fiber must yield its worker instead of blocking it. Plain
178 // external threads use a cv variable and do not need a fiber
179 // scheduler installed merely to wait for an A11 operation.
180 if (thread::GetPerThreadFiberPtr() != nullptr) {
181 const int selected = thread::SelectUntil(
182 deadline, {thread::OnCancel(), state_->event.OnEvent()});
183 if (selected == 0) {
184 return absl::CancelledError("Future wait cancelled");
185 }
186 if (selected < 0) {
187 return absl::DeadlineExceededError(
188 "Future was not ready before deadline");
189 }
190 } else {
191 thread::MutexLock lock(&state_->mu);
192 while (!state_->ready) {
193 if (state_->cv.WaitWithDeadline(&state_->mu, deadline) &&
194 !state_->ready) {
195 return absl::DeadlineExceededError(
196 "Future was not ready before deadline");
197 }
198 }
199 return *state_->result;
200 }
201
202 thread::MutexLock lock(&state_->mu);
203 if (!state_->ready) {
204 return absl::InternalError("Future wake-up did not publish a result");
205 }
206 return *state_->result;
207 }
208
216 absl::AnyInvocable<void(const absl::StatusOr<T>&)> callback) const {
217 if (callback == nullptr) {
218 return;
219 }
220 if (state_ == nullptr) {
221 const absl::StatusOr<T> invalid =
222 absl::FailedPreconditionError("Future is not valid");
223 internal::InvokeFutureCallback<T>(std::move(callback), invalid);
224 return;
225 }
226 const absl::StatusOr<T>* absl_nullable ready_result = nullptr;
227 {
228 thread::MutexLock lock(&state_->mu);
229 if (!state_->ready) {
230 state_->callbacks.push_back(std::move(callback));
231 return;
232 }
233 ready_result = &*state_->result;
234 }
235 internal::InvokeFutureCallback<T>(std::move(callback), *ready_result);
236 }
237
238 private:
239 void SetCancellationCallbackForExecutor(std::function<void()> cancel) {
240 if (state_ == nullptr) {
241 return;
242 }
243 thread::MutexLock lock(&state_->mu);
244 if (!state_->ready) {
245 state_->cancel = std::move(cancel);
246 }
247 }
248
249 explicit Future(std::shared_ptr<internal::FutureState<T>> state)
250 : state_(std::move(state)) {}
251
252 std::shared_ptr<internal::FutureState<T>> state_;
253
254 friend class Promise<T>;
255 template <typename U>
256 friend Future<U> Submit(absl::AnyInvocable<absl::StatusOr<U>() &&> work,
257 thread::TreeOptions tree_options);
258 template <typename U>
260 absl::AnyInvocable<absl::StatusOr<U>() &&> work,
261 std::function<void()> cancellation_hook,
262 thread::TreeOptions tree_options);
263};
264
273template <typename T>
274class Promise {
275 public:
276 Promise() : state_(std::make_shared<internal::FutureState<T>>()) {}
277
278 Promise(const Promise&) = delete;
279 Promise& operator=(const Promise&) = delete;
280
282 Promise(Promise&& other) noexcept : state_(std::move(other.state_)) {}
283
286 if (this != &other) {
287 Abandon();
288 state_ = std::move(other.state_);
289 }
290 return *this;
291 }
292
293 ~Promise() { Abandon(); }
294
296 [[nodiscard]] Future<T> future() const { return Future<T>(state_); }
297
299 absl::Status SetCancellationCallback(std::function<void()> cancel) {
300 if (state_ == nullptr) {
301 return absl::FailedPreconditionError("Promise is not valid");
302 }
303 thread::MutexLock lock(&state_->mu);
304 if (state_->ready) {
305 return absl::FailedPreconditionError("Promise is already complete");
306 }
307 state_->cancel = std::move(cancel);
308 return absl::OkStatus();
309 }
310
312 absl::Status SetValue(T value) {
313 return SetResult(absl::StatusOr<T>(std::move(value)));
314 }
315
317 absl::Status SetStatus(absl::Status status) {
318 if (status.ok()) {
319 return absl::InvalidArgumentError(
320 "SetStatus requires a non-OK status; use SetValue for success");
321 }
322 absl::StatusOr<T> result;
323 result.AssignStatus(std::move(status));
324 return SetResult(std::move(result));
325 }
326
328 absl::Status SetResult(absl::StatusOr<T> result) {
329 if (state_ == nullptr) {
330 return absl::FailedPreconditionError("Promise is not valid");
331 }
332 std::vector<absl::AnyInvocable<void(const absl::StatusOr<T>&)>> callbacks;
333 const absl::StatusOr<T>* absl_nullable published = nullptr;
334 {
335 thread::MutexLock lock(&state_->mu);
336 if (state_->ready) {
337 return absl::AlreadyExistsError("Promise has already been completed");
338 }
339 state_->result.emplace(std::move(result));
340 state_->ready = true;
341 state_->cancel = {};
342 published = &*state_->result;
343 callbacks.swap(state_->callbacks);
344 }
345 state_->event.Notify();
346 state_->cv.SignalAll();
347 for (auto& callback : callbacks) {
348 internal::InvokeFutureCallback<T>(std::move(callback), *published);
349 }
350 return absl::OkStatus();
351 }
352
353 private:
354 void Abandon() {
355 if (state_ == nullptr) {
356 return;
357 }
358 bool ready = false;
359 {
360 thread::MutexLock lock(&state_->mu);
361 ready = state_->ready;
362 }
363 // Never release the last state owner while its embedded mutex is locked.
364 if (ready) {
365 state_.reset();
366 return;
367 }
368 SetStatus(absl::CancelledError("Promise was abandoned")).IgnoreError();
369 state_.reset();
370 }
371
372 std::shared_ptr<internal::FutureState<T>> state_;
373};
374
376template <typename T>
378 Promise<T> promise;
379 Future<T> future = promise.future();
380 promise.SetResult(std::move(value)).IgnoreError();
381 return future;
382}
383
385template <typename T>
386Future<T> CompletedFuture(absl::StatusOr<T> result) {
387 Promise<T> promise;
388 Future<T> future = promise.future();
389 promise.SetResult(std::move(result)).IgnoreError();
390 return future;
391}
392
394template <typename T>
395Future<T> FailedFuture(absl::Status status) {
396 Promise<T> promise;
397 Future<T> future = promise.future();
398 promise.SetStatus(std::move(status)).IgnoreError();
399 return future;
400}
401
404
406inline Task ReadyTask() {
407 return ReadyFuture(Unit{});
408}
409
411inline Task FailedTask(absl::Status status) {
412 return FailedFuture<Unit>(std::move(status));
413}
414
415} // namespace a11
416
417#endif // A11_CONCURRENCY_FUTURE_H_
Shared handle to one asynchronous result.
Definition future.h:110
absl::StatusOr< T > Await(absl::Time deadline=absl::InfiniteFuture()) const
Wait for and return the result up to an absolute deadline.
Definition future.h:166
bool IsReady() const
Whether the producer has published either a value or an error.
Definition future.h:118
Future()=default
friend Future< U > Submit(absl::AnyInvocable< absl::StatusOr< U >() && > work, thread::TreeOptions tree_options)
absl::Status Cancel() const
Request cancellation from the operation producing this result.
Definition future.h:131
friend Future< U > SubmitWithCancellationHook(absl::AnyInvocable< absl::StatusOr< U >() && > work, std::function< void()> cancellation_hook, thread::TreeOptions tree_options)
bool valid() const
Whether this handle refers to shared completion state.
Definition future.h:115
void OnReady(absl::AnyInvocable< void(const absl::StatusOr< T > &)> callback) const
Run callback once when the result becomes available.
Definition future.h:215
Move-only producer for a Future result.
Definition future.h:274
absl::Status SetValue(T value)
Complete successfully with value.
Definition future.h:312
absl::Status SetStatus(absl::Status status)
Complete with a non-OK status.
Definition future.h:317
absl::Status SetResult(absl::StatusOr< T > result)
Complete with either a value or an error, waking every observer.
Definition future.h:328
Future< T > future() const
Return a consumer handle sharing this promise's completion state.
Definition future.h:296
Promise()
Definition future.h:276
Promise(Promise &&other) noexcept
Transfer responsibility for completing or abandoning the shared state.
Definition future.h:282
~Promise()
Definition future.h:293
absl::Status SetCancellationCallback(std::function< void()> cancel)
Install the operation invoked when a consumer calls Future::Cancel().
Definition future.h:299
Promise & operator=(const Promise &)=delete
Promise & operator=(Promise &&other) noexcept
Abandon this state, then take responsibility for other's state.
Definition future.h:285
Promise(const Promise &)=delete
thread::Mutex mu
Definition executor.cc:20
void InvokeFutureCallback(absl::AnyInvocable< void(const absl::StatusOr< T > &)> callback, const absl::StatusOr< T > &result)
Definition future.h:65
Definition action.cc:46
Task FailedTask(absl::Status status)
Return an already-failed Task.
Definition future.h:411
Future< T > FailedFuture(absl::Status status)
Return an already-failed future containing status.
Definition future.h:395
Future< T > ReadyFuture(T value)
Return an already-successful future containing value.
Definition future.h:377
Task ReadyTask()
Return an already-successful Task.
Definition future.h:406
Future< T > CompletedFuture(absl::StatusOr< T > result)
Return an already-completed future containing result.
Definition future.h:386
Future< T > Submit(absl::AnyInvocable< absl::StatusOr< T >() && > work, thread::TreeOptions tree_options)
Run status-returning work on the fiber pool and expose its Future.
Definition executor.h:66
Future< T > SubmitWithCancellationHook(absl::AnyInvocable< absl::StatusOr< T >() && > work, std::function< void()> cancellation_hook, thread::TreeOptions tree_options)
Run work on A11's fiber pool with application-specific cancellation.
Definition executor.h:30
Empty success value used by Future<Unit> operations that return no data.
Definition future.h:40
friend bool operator==(Unit, Unit)=default