SkillAgentSearch skills...

Asynqro

Futures and thread pool for C++ (with optional Qt support)

Install / Use

/learn @dkormalev/Asynqro
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

License Release Conan <br/> Travis-CI Build Status Appveyor Build Status<br/> Code Coverage Language grade: C/C++

Asynqro

Asynqro is a small library with purpose to make C++ programming easier by giving developers rich monadic Future API (mostly inspired by Future API in Scala language). This library is another implementation of ideas in https://github.com/opensoft/proofseed (now moved to asynqro usage, for historic purposes check tags before 02/25/19), but has much cleaner API, refined task scheduling logic and is not tied to any framework.

Dependencies

  • C++17: Should work with Clang >=6, GCC >=7 and MSVC >=16. Tested with Clang9 (travis), GCC9 (travis) and MSVC16 (VS2019, appveyor). Also tested for compatibility with clang 6.0 and gcc 7.4 on Ubuntu Bionic image (travis). Conan packages available with help of bincrafters.
  • CMake >= 3.12.0
  • GoogleTest. Will be automatically downloaded during cmake phase.
  • lcov >= 1.14. Used for code coverage calculation, not needed for regular build.
  • Optional Qt5 >= 5.10. It is not required though and by default asynqro is built without Qt support. There is no Qt dependency in library itself, but enabling it brings support for Qt containers, adds Future::fromQtSignal() and Future::fromQtFuture(). Also Future::wait() becomes guithread-aware.

Asynqro has two main parts:

Future/Promise

There are already a lot of implementations of Future mechanism in C++:

  • std::future - no API at all, except very basic operations like retrieve value and wait for it. There is a Concurrency TS with .then, but it is still not in the standard.
  • boost::future - almost the same as std::future.
  • QFuture - Mostly unusable outside of QtConcurrent without Qt private headers because there is no way to fill it from user code.
  • Folly - Folly futures are also inspired by Scala ones (but different ones, from Twitter framework) and have good API but are a part of huge framework which is too hard to use partially.
  • Many others not mentioned here

So why not to create another one?

Future-related part of asynqro contains:

All classes are reentrant and thread-safe.

All higher-order methods are exception-safe. If any exception happens inside function passed to such method, then Future will fail (or task will gracefully stop if it is runAndForget).

It is possible to use Future with movable-only classes (except sequence()). In this case resultRef() should be used instead of result().

Asynqro is intended to be used by including asynqro/asynqro header that includes asynqro/future.h and asynqro/tasks.h. It is also possible to include only asynqro/futures.h if task scheduling is not needed. simplefuture.h provides simple wrapper with std::any as failure type. All other headers except these three are considered as implementation and should not be included directly.

Good example of customizing Future to specific needs can be found in https://github.com/opensoft/proofseed/blob/develop/include/proofseed/asynqro_extra.h .

Promise

There is not a lot of methods in this class and its main usage is to generate Future object which later will be filled by this Promise at most one time. All subsequent fills will be ignored.

Future

This class shouldn't be instantiated directly in users code, but rather is obtained either from Promise or from tasks scheduling part. Also new Future object is returned from all transformation Future methods. It complies with functor and monad laws from FP and provides all operators required by them (successful/failed, map, flatMap).

Future is also sort of left-biased EitherT with result type as left side and failure type as right value (to provide failure reason). Sides were chosen non-canonical way (typical Either usually has right side as result and left as error) for compatibility purposes: std::expected type in C++ is sided the same way.

Almost all Future methods returns Future object, so they can be effectively chained. Futures are almost immutable. Almost because they will change there state at most one time when they are filled. Adding callbacks doesn't change behavior of other already applied callbacks (i.e. it will not change state of Future and/or its value).

if higher-order method is called on already filled Future it will be called (in case of matching status) immediately in the same thread. If it is not yet filled, it will be put to queue, which will be called (in non-specified order) on Future filling in thread that filled the Future.

Future API

  • successful - T->Future<T, FailureType> creates new Future object filled as successful with provided value
  • failed - FailureType->Future<T, FailureType> creates new Future object filled as failed with provided reason
  • fromQtSignal - (QObject, Signal)->Future<T, FailureType> creates new Future object that will be filled when signal emits. T here should be either bool or same type as signal parameter (if signal has more than one parameter T should be std::tuple)
  • fromQtFuture - QFuture<OtherT>->Future<T, FailureType> creates new Future object that will be filled with QFuture result. OtherT and T must follow next rules:
    • if OtherT is void then T must be bool
    • if T is bool and OtherT is not convertible to bool then result will always be true
    • if T is a container of OtherT all results from QFuture will be used
    • if nothing above is true then OtherT must be convertible to T and in this case first result from QFuture will be used
  • wait - waits for Future to be filled (either as successful or as failed) if it is not yet filled with optional timeout
  • isCompleted/isFailed/isSucceeded - returns current state of Future
  • result/resultRef/failureReason - returns result of Future or failure reason. Will wait for Future to be filled if it isn't already.
  • onSuccess - (T->void)->Future<T, FailureType> adds a callback for successful case.
  • onFailure - (FailureType->void)->Future<T, FailureType> adds a callback for failure case.
  • filter - (T->bool, FailureType)->Future<T, FailureType> fails Future if function returns false for filled value.
  • map - (T->U)->Future<U, FailureType> transforms Future inner type. Also available as >> operator.
  • mapFailure - (FailureType->OtherFailureType)->Future<T, OtherFailureType> transforms Future failure type.
  • flatMap - (T->Future<U, FailureType>)->Future<U, FailureType> transforms Future inner type. Also available as >> operator.
  • andThen - (void->Future<U, FailureType>)->Future<U, FailureType> shortcut for flatMap if value of previous Future doesn't matter. Also available as >> operator.
  • andThenValue - U->Future<U, FailureType> shortcut for andThen if all we need is to replace value of successful Future with some already known value.
  • innerReduce/innerMap/innerFilter/innerFlatten - applicable only for Future with sequence inner type. Allows to modify sequence by reducing, mapping, filtering or flattening it.
  • recover - (FailureType->T)->Future<T, FailureType> transform failed Future to successful
  • recoverWith - (FailureType->Future<T, FailureType>)->Future<T, FailureType> the same as recover, but allows to return Future in callback
  • recoverValue - T->Future<T, FailureType> shortcut for recover when we just need to replace with some already known value
  • zip - (Future<U, FailureType>, ...) -> Future<std::tuple<T, U, ...>, FailureType> combines values from different Futures. If any of the Futures already have tuple as inner type, then U will be list of types from this std::tuple (so resulting tuple will be a flattened one). If zipped Futures have different FailureTypes then they will be combined in std::variant (with flattening if some of FailureTypes are already variants). Also available as + operator.
  • zipValue - U->Future<std::tuple<T, U>, FailureType> - shortcut for zip with already known value.
  • sequence - Sequence<Future<T, FailureType>> -> Future<Sequence<T>, FailureType> transformation from sequence of Futures to single Future.
  • sequenceWithFailures - Sequence<Future<T, FailureType>> -> Future<std::pair<AssocSequence<Sequence::size_type, T>, AssocSequence<Sequence::size_type, FailureType>>, FailureType> transformation from sequence of Futures to single Future with separate containers for successful Futures and failed ones. AssocSequence can be set as optional type parameter.

CancelableFuture

API of

View on GitHub
GitHub Stars131
CategoryCustomer
Updated27d ago
Forks18

Languages

C++

Security Score

100/100

Audited on Mar 12, 2026

No findings