AUI Framework  master
Cross-platform module-based framework for developing C++20 desktop applications
AFuture.h
1/*
2 * AUI Framework - Declarative UI toolkit for modern C++20
3 * Copyright (C) 2020-2024 Alex2772 and Contributors
4 *
5 * SPDX-License-Identifier: MPL-2.0
6 *
7 * This Source Code Form is subject to the terms of the Mozilla Public
8 * License, v. 2.0. If a copy of the MPL was not distributed with this
9 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 */
11
12#pragma once
13
14#include <exception>
15#include <thread>
16#include <utility>
17#include "AUI/Traits/concepts.h"
18#include "AUI/Util/ABitField.h"
19#if AUI_COROUTINES
20#include <coroutine>
21#endif
22
23#include <atomic>
24#include <functional>
25#include <optional>
26#include "AConditionVariable.h"
27#include "AMutex.h"
28#include <AUI/Common/SharedPtrTypes.h>
29#include <AUI/Common/AString.h>
30#include <AUI/Common/AException.h>
31#include <AUI/Logging/ALogger.h>
32#include <AUI/Reflect/AReflect.h>
33
34class AThreadPool;
35
36
38
39public:
40 AInvocationTargetException(const AString& message = {}, std::exception_ptr causedBy = std::current_exception()):
41 AException(message, std::move(causedBy), AStacktrace::capture(3)) {}
42
43
44 ~AInvocationTargetException() noexcept override = default;
45};
46
47
52AUI_ENUM_FLAG(AFutureWait) {
53 JUST_WAIT = 0b00,
54 ALLOW_STACKFUL_COROUTINES = 0b10,
55
59 ALLOW_TASK_EXECUTION_IF_NOT_PICKED_UP = 0b01,
60 DEFAULT = ALLOW_STACKFUL_COROUTINES | ALLOW_TASK_EXECUTION_IF_NOT_PICKED_UP,
61};
62
63
64namespace aui::impl::future {
69 template<typename Inner>
71
72 explicit CancellationWrapper(_unique<Inner> wrapped) : wrapped(std::move(wrapped)) {}
73
75 wrapped->cancel();
76 wrapped->waitForTask();
77 }
78
79 [[nodiscard]]
80 const _unique<Inner>& ptr() noexcept {
81 return wrapped;
82 }
83
84 Inner* operator->() const noexcept {
85 return wrapped.get();
86 }
87
88 private:
89 _unique<Inner> wrapped;
90 };
91
92 template<typename T>
94 using type = std::function<void(const T& value)>;
95 };
96
97 template<typename T>
99 using type = T&;
100 };
101
102 template<>
103 struct FutureReturnType<void> {
104 using type = void;
105 };
106
107
108 template<>
109 struct OnSuccessCallback<void> {
110 using type = std::function<void()>;
111 };
112 template<typename Value = void>
113 class Future
114 {
115 public:
116 static constexpr bool isVoid = std::is_same_v<void, Value>;
117 using TaskCallback = std::function<Value()>;
118
119 using OnSuccessCallback = typename OnSuccessCallback<Value>::type;
120
121 struct Inner {
122 bool interrupted = false;
126 std::conditional_t<isVoid, bool, AOptional<Value>> value;
134 TaskCallback task;
135 OnSuccessCallback onSuccess;
136 std::function<void(const AException& exception)> onError;
137 _<AAbstractThread> thread;
138 bool cancelled = false;
139
140 explicit Inner(std::function<Value()> task) noexcept: task(std::move(task)) {
141 if constexpr(isVoid) {
142 value = false;
143 }
144 }
145
146 void waitForTask() noexcept {
147 std::unique_lock lock(mutex);
148 bool rethrowInterrupted = false;
149 while ((thread) && !hasResult() && !cancelled) {
150 try {
151 cv.wait(lock);
152 } catch (const AThread::Interrupted&) {
153 rethrowInterrupted = true;
154 }
155 }
156 if (rethrowInterrupted) {
157 AThread::current()->interrupt();
158 }
159 }
160
161 [[nodiscard]]
162 bool isWaitNeeded() noexcept {
163 return (thread || !cancelled) && !hasResult();
164 }
165
166 [[nodiscard]]
167 bool hasResult() const noexcept {
168 return value || exception || interrupted;
169 }
170
171 [[nodiscard]]
172 bool hasValue() const noexcept {
173 return bool(value);
174 }
175
176 bool setThread(_<AAbstractThread> thr) noexcept {
177 std::unique_lock lock(mutex);
178 if (cancelled) return true;
179 if (thread) return true;
180 thread = std::move(thr);
181 return false;
182 }
183
184 void wait(const _weak<CancellationWrapper<Inner>>& innerWeak, ABitField<AFutureWait> flags = AFutureWait::DEFAULT) noexcept;
185
186 void cancel() noexcept {
187 std::unique_lock lock(mutex);
188 if (!cancelled) {
189 cancelled = true;
190 if (thread && !hasResult()) {
191 thread->interrupt();
192 }
193 }
194 }
195
203 /*
204 * We should assume that this == nullptr or invalid.
205 * We can assert that this pointer is safe only if we hold at least one shared_ptr.
206 * It allows callers to pass a weak_ptr in any state.
207 */
208 if (auto innerCancellation = innerWeak.lock()) {
209 if (value) return false;
210 auto& inner = innerCancellation->ptr();
211 if (inner->setThread(AThread::current())) return false;
212 try {
213 if (task == nullptr) { // task is executed in wait() function
214 return false;
215 }
216 std::unique_lock lock(mutex);
217 if (task == nullptr) { // task is executed in wait() function
218 return false;
219 }
220 auto func = std::move(task);
221 AUI_ASSERT(bool(func));
222 lock.unlock();
223 innerCancellation = nullptr;
224 if constexpr(isVoid) {
225 func();
226 if (auto sharedPtrLock = innerWeak.lock()) {
227 lock.lock();
228 value = true;
229 cv.notify_all();
231
232 (void)sharedPtrLock; // sharedPtrLock is *used*
233 if (lock.owns_lock()) lock.unlock(); // unlock earlier because destruction of shared_ptr may cause deadlock
234 }
235 } else {
236 auto result = func();
237 if (auto sharedPtrLock = innerWeak.lock()) {
238 lock.lock();
239 value = std::move(result);
240 cv.notify_all();
242
243 (void)sharedPtrLock; // sharedPtrLock is *used*
244 if (lock.owns_lock()) lock.unlock(); // unlock earlier because destruction of shared_ptr may cause deadlock
245 }
246 }
247 } catch (const AThread::Interrupted&) {
248 if (auto sharedPtrLock = innerWeak.lock()) {
249 inner->reportInterrupted();
250 }
251 throw;
252 } catch (...) {
253 if (auto sharedPtrLock = innerWeak.lock()) {
254 inner->reportException();
255 }
256 return false;
257 }
258 }
259 return true;
260 }
261
262 void reportInterrupted() noexcept {
263 std::unique_lock lock(mutex);
264 interrupted = true;
265 cv.notify_all();
266 }
267
268 void reportException(std::exception_ptr causedBy = std::current_exception()) noexcept {
269 if (cancelled) {
270 return;
271 }
272 std::unique_lock lock(mutex);
273 exception.emplace("exception reported", std::move(causedBy));
274 cv.notify_all();
275 if (!onError) {
276 return;
277 }
278 auto localOnError = std::move(onError);
279 lock.unlock();
280 localOnError(*exception);
281 }
282
283
293 void notifyOnSuccessCallback(std::unique_lock<decltype(mutex)>& lock) noexcept {
294 AUI_ASSERT(lock.owns_lock());
295 if (cancelled) {
296 return;
297 }
298 if (value && onSuccess) {
299 auto localOnSuccess = std::move(onSuccess);
300 onSuccess = nullptr;
301 lock.unlock();
302 invokeOnSuccessCallback(localOnSuccess);
303 }
304 }
305
306 template<typename F>
307 void invokeOnSuccessCallback(F&& f) {
308 try {
309 if constexpr (isVoid) {
310 f();
311 } else {
312 f(*value);
313 }
314 } catch (const AException& e) {
315 ALogger::err("AFuture") << "AFuture onSuccess thrown an exception: " << e;
316 }
317 }
318
319 template<typename Callback>
320 void addOnSuccessCallback(Callback&& callback) {
321 if constexpr (isVoid) {
322 if (onSuccess) {
323 onSuccess = [prev = std::move(onSuccess),
324 callback = std::forward<Callback>(callback)]() mutable {
325 prev();
326 callback();
327 };
328 } else {
329 onSuccess = [callback = std::forward<Callback>(callback)]() mutable { callback(); };
330 }
331 } else {
332 if (onSuccess) {
333 onSuccess = [prev = std::move(onSuccess),
334 callback = std::forward<Callback>(callback)](const Value& v) mutable {
335 prev(v);
336 callback(v);
337 };
338 } else {
339 onSuccess = [callback = std::forward<Callback>(callback)](const Value& v) mutable {
340 callback(v);
341 };
342 }
343 }
344 }
345
346 template<typename Callback>
347 void addOnErrorCallback(Callback&& callback) {
348 if (onError) {
349 onError = [prev = std::move(onError),
350 callback = std::forward<Callback>(callback)](const AException& v) {
351 prev(v);
352 callback(v);
353 };
354 } else {
355 onError = [callback = std::forward<Callback>(callback)](const AException& v) { callback(v); };
356 }
357 }
358 };
359
360#if AUI_COROUTINES
364 struct CoPromiseType;
365#endif
366
367 protected:
369
370
371 public:
376 Future(TaskCallback task = nullptr): mInner(_new<CancellationWrapper<Inner>>((_unique<Inner>)(new Inner(std::move(task))))) {}
377
378 [[nodiscard]]
379 const _<CancellationWrapper<Inner>>& inner() const noexcept {
380 return mInner;
381 }
382
386 [[nodiscard]]
387 bool isWaitNeeded() const noexcept {
388 return (*mInner)->isWaitNeeded();
389 }
390
394 [[nodiscard]]
395 bool hasResult() const noexcept {
396 return (*mInner)->hasResult();
397 }
398
403 [[nodiscard]]
404 bool hasValue() const noexcept {
405 return (*mInner)->hasValue();
406 }
407
408 void reportException() const noexcept {
409 (*mInner)->reportException();
410 }
411
412 template<typename Callback>
413 void onSuccess(Callback&& callback) const {
414 if (hasValue()) { // cheap lookahead
415 (*mInner)->invokeOnSuccessCallback(std::forward<Callback>(callback));
416 return;
417 }
418 std::unique_lock lock((*mInner)->mutex);
419 if (hasValue()) { // not so cheap, but cheapier than adding the callback to inner
420 (*mInner)->invokeOnSuccessCallback(std::forward<Callback>(callback));
421 return;
422 }
423 (*mInner)->addOnSuccessCallback(std::forward<Callback>(callback));
424 }
425
426 template<aui::invocable<const AException&> Callback>
427 void onError(Callback&& callback) const {
428 std::unique_lock lock((*mInner)->mutex);
429 (*mInner)->addOnErrorCallback(std::forward<Callback>(callback));
430 }
431
435 template<aui::invocable Callback>
436 void onFinally(Callback&& callback) const {
437 std::unique_lock lock((*mInner)->mutex);
438 (*mInner)->addOnSuccessCallback([callback](const auto&...) { callback(); });
439 (*mInner)->addOnErrorCallback([callback = std::move(callback)](const auto&...) { callback(); });
440 (*mInner)->notifyOnSuccessCallback(lock);
441 }
442
452 void cancel() const noexcept {
453 (*mInner)->cancel();
454 }
455
456 void reportInterrupted() const {
457 (*mInner)->reportInterrupted();
458 }
459
465 void wait(AFutureWait flags = AFutureWait::DEFAULT) const {
466 (*mInner)->wait(mInner, flags);
467 checkForSelfWait();
468 }
469
478 typename FutureReturnType<Value>::type get(AFutureWait flags = AFutureWait::DEFAULT) const {
480
481 (*mInner)->wait(mInner, flags);
482
484 if ((*mInner)->exception) {
485 throw *(*mInner)->exception;
486 }
487 if ((*mInner)->interrupted) {
488 throw AInvocationTargetException("Future execution interrupted");
489 }
490 checkForSelfWait();
491 if constexpr(!isVoid) {
492 return *(*mInner)->value;
493 }
494 }
495
496
505 typename FutureReturnType<Value>::type operator*() const {
506 return **const_cast<Future*>(this);
507 }
508
517 typename FutureReturnType<Value>::type operator*() {
518 return get();
519 }
520
529 Value* operator->() const {
530 return &operator*();
531 }
532
533 private:
534 void checkForSelfWait() const {
535 if (!(*mInner)->hasResult() && AThread::current() == (*mInner)->thread) {
536 throw AException("self wait?");
537 }
538 }
539 };
540
541}
542
543
619template<typename T = void>
620class AFuture final: public aui::impl::future::Future<T> {
621private:
622 using super = typename aui::impl::future::Future<T>;
623
624public:
625 using Task = typename super::TaskCallback;
626#if AUI_COROUTINES
627 using promise_type = typename super::CoPromiseType;
628#endif
630
631 explicit AFuture(T immediateValue): super() {
632 auto& inner = (*super::mInner);
633 inner->value = std::move(immediateValue);
634 }
635 AFuture(Task task = nullptr) noexcept: super(std::move(task)) {}
636 ~AFuture() = default;
637
638 AFuture(const AFuture&) = default;
639 AFuture(AFuture&&) noexcept = default;
640
641 AFuture& operator=(const AFuture&) = default;
642 AFuture& operator=(AFuture&&) noexcept = default;
643
650 void supplyValue(T v) const noexcept {
651 auto& inner = (*super::mInner);
652 AUI_ASSERTX(inner->task == nullptr, "task is already provided");
653
654 std::unique_lock lock(inner->mutex);
655 inner->value = std::move(v);
656 inner->cv.notify_all();
657 inner->notifyOnSuccessCallback(lock);
658 }
659
663 void supplyException(std::exception_ptr causedBy = std::current_exception()) const noexcept {
664 auto& inner = (*super::mInner);
665 inner->reportException(std::move(causedBy));
666 }
667
668 AFuture& operator=(std::nullptr_t) noexcept {
669 super::mInner = nullptr;
670 return *this;
671 }
672
673 [[nodiscard]]
674 bool operator==(const AFuture& r) const noexcept {
675 return super::mInner == r.mInner;
676 }
677
697 template<aui::invocable<const T&> Callback>
698 const AFuture& onSuccess(Callback&& callback) const noexcept {
699 super::onSuccess(std::forward<Callback>(callback));
700 return *this;
701 }
702
722 template<aui::invocable<const AException&> Callback>
723 const AFuture& onError(Callback&& callback) const noexcept {
724 super::onError(std::forward<Callback>(callback));
725 return *this;
726 }
727
731 template<aui::invocable Callback>
732 const AFuture& onFinally(Callback&& callback) const noexcept {
733 super::onFinally(std::forward<Callback>(callback));
734 return *this;
735 }
736
740 template<aui::invocable<const T&> Callback>
741 auto map(Callback&& callback) -> AFuture<decltype(callback(std::declval<T>()))> const {
743 onSuccess([result, callback = std::forward<Callback>(callback)](const T& v) {
744 result.supplyValue(callback(v));
745 });
746 onError([result](const AException& v) {
747 try {
748 throw v;
749 } catch (...) {
750 result.reportException();
751 }
752 });
753 return result;
754 }
755};
756
757template<>
758class AFuture<void> final: public aui::impl::future::Future<void> {
759private:
760 using T = void;
761 using super = typename aui::impl::future::Future<T>;
762
763public:
764 using Task = typename super::TaskCallback;
765#if AUI_COROUTINES
766 using promise_type = typename super::CoPromiseType;
767#endif
768 using Inner = decltype(std::declval<super>().inner());
769
770 AFuture(Task task = nullptr) noexcept: super(std::move(task)) {}
771 ~AFuture() = default;
772
773 AFuture(const AFuture&) = default;
774 AFuture(AFuture&&) noexcept = default;
775
776 AFuture& operator=(const AFuture&) = default;
777 AFuture& operator=(AFuture&&) noexcept = default;
778
782 void supplyException(std::exception_ptr causedBy = std::current_exception()) const noexcept {
783 auto& inner = (*super::mInner);
784 inner->reportException(std::move(causedBy));
785 }
786
792 void supplyValue() const noexcept {
793 auto& inner = (*super::mInner);
794 AUI_ASSERTX(inner->task == nullptr, "task is already provided");
795
796 std::unique_lock lock(inner->mutex);
797 inner->value = true;
798 inner->cv.notify_all();
799 inner->notifyOnSuccessCallback(lock);
800 }
801
802 AFuture& operator=(std::nullptr_t) noexcept {
803 super::mInner = nullptr;
804 return *this;
805 }
806
807 [[nodiscard]]
808 bool operator==(const AFuture& r) const noexcept {
809 return super::mInner == r.mInner;
810 }
811
831 template<aui::invocable Callback>
832 const AFuture& onSuccess(Callback&& callback) const {
833 super::onSuccess(std::forward<Callback>(callback));
834 return *this;
835 }
836
856 template<aui::invocable<const AException&> Callback>
857 const AFuture& onError(Callback&& callback) const {
858 super::onError(std::forward<Callback>(callback));
859 return *this;
860 }
861
865 template<aui::invocable Callback>
866 const AFuture& onFinally(Callback&& callback) const {
867 super::onFinally(std::forward<Callback>(callback));
868 return *this;
869 }
870};
871
872
873#include <AUI/Thread/AThreadPool.h>
874#include <AUI/Common/AException.h>
875
876template <typename Value>
877void aui::impl::future::Future<Value>::Inner::wait(const _weak<CancellationWrapper<Inner>>& innerWeak,
878 ABitField<AFutureWait> flags) noexcept {
879 if (hasResult()) return; // cheap check
880 std::unique_lock lock(mutex);
881 try {
882 if ((thread || !cancelled) && !hasResult() && flags & AFutureWait::ALLOW_TASK_EXECUTION_IF_NOT_PICKED_UP && task) {
883 // task is not have picked up by the threadpool; execute it here
884 lock.unlock();
885 if (tryExecute(innerWeak)) {
886 return;
887 }
888 lock.lock();
889 }
890
891 if (flags & AFutureWait::ALLOW_STACKFUL_COROUTINES) {
892 if (auto threadPoolWorker = _cast<AThreadPool::Worker>(AThread::current())) {
893 if (hasResult()) return; // for sure
894 AUI_ASSERT(lock.owns_lock());
895 auto callback = [threadPoolWorker](auto&&...) {
896 threadPoolWorker->threadPool().wakeUpAll();
897 };
898 addOnSuccessCallback(callback);
899 addOnErrorCallback(callback);
900
901 threadPoolWorker->loop([&] {
902 if (lock.owns_lock())
903 lock.unlock();
904 return !hasResult();
905 });
906
907 return;
908 }
909 }
910 while ((thread || !cancelled) && !hasResult()) {
911 if (thread == AThread::current()) [[unlikely]] {
912 // self wait?
913 return;
914 }
915 cv.wait(lock);
916 }
917 } catch (const AThread::Interrupted& e) {
918 e.needRethrow();
919 }
920}
921
922#if AUI_COROUTINES
923template<typename Value>
924struct aui::impl::future::Future<Value>::CoPromiseType {
925 AFuture<Value> future;
926 auto initial_suspend() const noexcept
927 {
928 return std::suspend_never{};
929 }
930
931 auto final_suspend() const noexcept
932 {
933 return std::suspend_never{};
934 }
935 auto unhandled_exception() const noexcept {
936 future.supplyException();
937 }
938
939 const AFuture<Value>& get_return_object() const noexcept {
940 return future;
941 }
942
943 void return_value(Value v) const noexcept {
944 future.supplyValue(std::move(v));
945 }
946};
947
948template<typename T>
949auto operator co_await(AFuture<T> future) {
950 struct Awaitable {
951 AFuture<T> future;
952
953 bool await_ready() const noexcept {
954 return future.hasResult();
955 }
956
957 T await_resume() {
958 return *future;
959 }
960
961
962 void await_suspend(std::coroutine_handle<> h)
963 {
964 future.onSuccess([h](const int&) {
965 h.resume();
966 });
967
968 future.onError([h](const AException&) {
969 h.resume();
970 });
971 }
972 };
973
974 return Awaitable{ std::move(future) };
975}
976#endif
Bit field implementation.
Definition: ABitField.h:20
Represents a condition variable.
Definition: AConditionVariable.h:24
void notify_all() noexcept
Definition: AConditionVariable.h:62
Abstract AUI exception.
Definition: AException.h:29
const AFuture & onError(Callback &&callback) const
Add onSuccess callback to the future.
Definition: AFuture.h:857
const AFuture & onFinally(Callback &&callback) const
Adds the callback to both onSuccess and onResult.
Definition: AFuture.h:866
void supplyException(std::exception_ptr causedBy=std::current_exception()) const noexcept
Stores an exception from std::current_exception to the future.
Definition: AFuture.h:782
const AFuture & onSuccess(Callback &&callback) const
Add onSuccess callback to the future.
Definition: AFuture.h:832
void supplyValue() const noexcept
Pushes "success" result.
Definition: AFuture.h:792
Represents a value that will be available at some point in the future.
Definition: AFuture.h:620
const AFuture & onSuccess(Callback &&callback) const noexcept
Add onSuccess callback to the future.
Definition: AFuture.h:698
const AFuture & onError(Callback &&callback) const noexcept
Add onError callback to the future.
Definition: AFuture.h:723
auto map(Callback &&callback) -> AFuture< decltype(callback(std::declval< T >()))> const
Maps this AFuture to another type of AFuture.
Definition: AFuture.h:741
void supplyValue(T v) const noexcept
Pushes the result to AFuture.
Definition: AFuture.h:650
void supplyException(std::exception_ptr causedBy=std::current_exception()) const noexcept
Stores an exception from std::current_exception to the future.
Definition: AFuture.h:663
const AFuture & onFinally(Callback &&callback) const noexcept
Adds the callback to both onSuccess and onResult.
Definition: AFuture.h:732
Definition: AFuture.h:37
Utility wrapper implementing the stack-allocated (fast) optional idiom.
Definition: AOptional.h:32
Synchronization primitive that is implemented with atomic values instead of doing syscalls.
Definition: AMutex.h:65
static AStacktrace capture(unsigned skipFrames=0, unsigned maxFrames=128) noexcept
Creates stacktrace of the current thread.
Definition: AStacktraceImpl.cpp:156
Represents a Unicode character string.
Definition: AString.h:37
Thread pool implementation.
Definition: AThreadPool.h:33
Exception that is thrown by AThread::interruptionPoint(), if interruption is requested for this threa...
Definition: AThread.h:181
void needRethrow() const noexcept
Schedules AThread::Interrupted exception to the next interruption point. Sometimes you could not thro...
Definition: AThread.h:188
static _< AAbstractThread > current()
Definition: AThread.cpp:221
static void interruptionPoint()
Interruption point.
Definition: AThread.cpp:232
An std::weak_ptr with AUI extensions.
Definition: SharedPtrTypes.h:177
Definition: AFuture.h:114
bool isWaitNeeded() const noexcept
Definition: AFuture.h:387
bool hasValue() const noexcept
Definition: AFuture.h:404
FutureReturnType< Value >::type operator*()
Returns the supplyValue from the another thread. Sleeps if the supplyValue is not currently available...
Definition: AFuture.h:517
void onFinally(Callback &&callback) const
Adds the callback to both onSuccess and onResult.
Definition: AFuture.h:436
Value * operator->() const
Returns the supplyValue from the another thread. Sleeps if the supplyValue is not currently available...
Definition: AFuture.h:529
void cancel() const noexcept
Cancels the AFuture's task.
Definition: AFuture.h:452
void wait(AFutureWait flags=AFutureWait::DEFAULT) const
Sleeps if the supplyValue is not currently available.
Definition: AFuture.h:465
bool hasResult() const noexcept
Definition: AFuture.h:395
Future(TaskCallback task=nullptr)
Definition: AFuture.h:376
FutureReturnType< Value >::type get(AFutureWait flags=AFutureWait::DEFAULT) const
Returns the supplyValue from the another thread. Sleeps if the supplyValue is not currently available...
Definition: AFuture.h:478
FutureReturnType< Value >::type operator*() const
Returns the task result from the another thread. Sleeps if the task result is not currently available...
Definition: AFuture.h:505
AUI_ENUM_FLAG(ASide)
Describes sides of a 2D rectangle.
Definition: ASide.h:24
#define AUI_ASSERT(condition)
Asserts that the passed condition evaluates to true.
Definition: Assert.h:55
#define AUI_ASSERTX(condition, what)
Asserts that the passed condition evaluates to true. Adds extra message string.
Definition: Assert.h:74
@ DEFAULT
There's no concrete input action. Let the OS decide which action is the most appropriate.
An std::weak_ptr with AUI extensions.
Definition: SharedPtrTypes.h:51
Definition: AFuture.h:98
Definition: AFuture.h:121
std::conditional_t< isVoid, bool, AOptional< Value > > value
Definition: AFuture.h:126
void notifyOnSuccessCallback(std::unique_lock< decltype(mutex)> &lock) noexcept
Calls onSuccess callback.
Definition: AFuture.h:293
bool tryExecute(const _weak< CancellationWrapper< Inner > > &innerWeak)
Executes the task stored in the future. Using weak_ptr to internal object in order to make possible F...
Definition: AFuture.h:202
ASpinlockMutex mutex
Definition: AFuture.h:132
Definition: AFuture.h:93
Definition: SharedPtrTypes.h:114