AUI Framework  develop
Cross-platform base for C++ UI apps
Loading...
Searching...
No Matches
AFuture< T > Class Template Referencefinal

Represents a value that will be available at some point in the future. More...

#include <AUI/Thread/AFuture.h>

Detailed Description

template<typename T = void>
class AFuture< T >
Template Parameters
Tresult type (void is default)
Warning
This API is experimental. Experimental APIs are likely to contain bugs, might be changed or removed in the future.
AFuture is used as a result for asynchronous functions.

AFuture is returned by AUI_THREADPOOL keyword, which is used to perform heavy operations in a background thread.

AFuture<int> theFuture = AUI_THREADPOOL {
  AThread::sleep(1000); // long operation
  return 123;
};
...
cout << *theFuture; // waits for the task, outputs 123
static void sleep(std::chrono::milliseconds duration)
Sleep for specified duration. Most operation systems guarantee that elasped time will be greater than...
#define AUI_THREADPOOL
Executes following {} block asynchronously in the global thread pool. Unlike AUI_THREADPOOL_X,...
Definition kAUI.h:329

If your operation consists of complex future sequences, you have multiple options:

  1. Use stackful coroutines. That is, you can use operator* and get() methods (blocking value acquiring) within a threadpool thread (including the one that runs AUI_THREADPOOL 's body). If value is not currently available, these methods temporarily return the thread to threadpool, effeciently allowing it to execute other tasks.
    Note
    Be aware fot std::unique_lock and similar RAII-based lock functions when performing blocking value acquiring operation.
  2. Use stackless coroutines. C++20 introduced coroutines language feature. That is, you can use co_await operator to AFuture value:
    AFuture<int> longOperation();
    AFuture<int> myFunction() {
      int resultOfLongOperation = co_await longOperation();
      return resultOfLongOperation + 1;
    }
  3. Use AComplexFutureOperation. This class creates AFuture (root AFuture) and forwards all exceptions to the root AFuture. This method is not recommended for trivial usecases, as it requires you to extensivly youse onSuccess method in order to get and process AFuture result, leading your code to hardly maintainable spaghetti.
For rare cases, you can default-construct AFuture and the result can be supplied manually with the supplyValue() method:
@code{cpp}
AFuture<int> theFuture;
AThread t([=] {
  AThread::sleep(1000); // long operation
  theFuture.supplyValue(123);
});
t.start();
cout << *theFuture; // 123
Represents a value that will be available at some point in the future.
Definition AFuture.h:621
void supplyValue(T v) const noexcept
Pushes the result to AFuture.
Definition AFuture.h:651
Represents a user-defined thread.
Definition AThread.h:172
type_of< T > t
Selects views that are of the specified C++ types.
Definition type_of.h:71
Note
Be aware of exceptions or control flow keywords! If you don't pass the result, AFuture will always stay unavailable, thus all waiting code will wait indefinitely long, leading to resource leaks (CPU and memory). Consider using one of suggested methods of usage instead.

AFuture provides a set of functions for both "value emitting" side: supplyValue(), supplyException(), and "value receiving" side: operator->(), operator*(), get().

When AFuture's operation is completed it calls either onSuccess() or onError(). These callbacks are excepted to be called in any case. Use onFinally() to handle both.

AFuture is a shared_ptr-based wrapper so it can be easily copied, pointing to the same task.

If all AFutures of the task are destroyed, the task is cancelled. If the task is executing when cancel() is called, AFuture waits for the task, however, task's thread is still requested for interrupt. It guarantees that your task cannot be executed or be executing when AFuture destroyed and allows to efficiently utilize c++'s RAII feature.

To manage multiple AFutures, use AAsyncHolder or AFutureSet classes.

Note
AFuture implements work-stealing algorithm to prevent deadlocks and optimizaze thread usage: when waiting for result, AFuture may execute the task (if not default-constructed) on the caller thread instead of waiting. See AFuture::wait for details.

Member Function Documentation

> All members, including inherited

◆ onError()

template<typename T = void>
template<aui::invocable< const AException & > Callback>
const AFuture & AFuture< T >::onError ( Callback && callback) const
inlinenoexcept

The callback will be called on the worker's thread when the async task is returned a result.

onError does not expand AFuture's lifespan, so when AFuture becomes invalid, onSuccess would not be called.

Note
To expand lifespan, create an AAsyncHolder inside your window or object; then put the instance of AFuture there. Example:
...
private:
  AAsyncHolder mAsync;
...
mAsync << functionReturningFuture().onSuccess(...); // or onError
Holds a set of futures keeping them valid.
Definition AAsyncHolder.h:31

◆ onSuccess()

template<typename T = void>
template<aui::invocable< const T & > Callback>
const AFuture & AFuture< T >::onSuccess ( Callback && callback) const
inlinenoexcept

The callback will be called on the worker's thread when the async task is returned a result.

onSuccess does not expand AFuture's lifespan, so when AFuture becomes invalid, onSuccess would not be called.

Note
To expand lifespan, create an AAsyncHolder inside your window or object; then put the instance of AFuture there. Example:
...
private:
  AAsyncHolder mAsync;
...
mAsync << functionReturningFuture().onSuccess(...); // or onError

◆ supplyValue()

template<typename T = void>
void AFuture< T >::supplyValue ( T v) const
inlinenoexcept
Parameters
vvalue

After AFuture grabbed the value, supplyValue calls onSuccess listeners with the new value.