mirror of
https://github.com/NohamR/RMHook-Win.git
synced 2026-05-25 12:27:12 +00:00
1165 lines
39 KiB
C++
1165 lines
39 KiB
C++
// Copyright (C) 2020 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
|
|
#ifndef QFUTURE_H
|
|
#error Do not include qfuture_impl.h directly
|
|
#endif
|
|
|
|
#if 0
|
|
#pragma qt_sync_skip_header_check
|
|
#pragma qt_sync_stop_processing
|
|
#endif
|
|
|
|
#include <QtCore/qglobal.h>
|
|
#include <QtCore/qfutureinterface.h>
|
|
#include <QtCore/qthreadpool.h>
|
|
#include <QtCore/qexception.h>
|
|
#include <QtCore/qpointer.h>
|
|
#include <QtCore/qpromise.h>
|
|
|
|
#include <memory>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
//
|
|
// forward declarations
|
|
//
|
|
template<class T>
|
|
class QFuture;
|
|
template<class T>
|
|
class QFutureInterface;
|
|
template<class T>
|
|
class QPromise;
|
|
|
|
namespace QtFuture {
|
|
|
|
enum class Launch { Sync, Async, Inherit };
|
|
|
|
template<class T>
|
|
struct WhenAnyResult
|
|
{
|
|
qsizetype index = -1;
|
|
QFuture<T> future;
|
|
};
|
|
|
|
// Deduction guide
|
|
template<class T>
|
|
WhenAnyResult(qsizetype, const QFuture<T> &) -> WhenAnyResult<T>;
|
|
}
|
|
|
|
namespace QtPrivate {
|
|
|
|
template<class T>
|
|
using EnableForVoid = std::enable_if_t<std::is_same_v<T, void>>;
|
|
|
|
template<class T>
|
|
using EnableForNonVoid = std::enable_if_t<!std::is_same_v<T, void>>;
|
|
|
|
template<typename F, typename Arg, typename Enable = void>
|
|
struct ResultTypeHelper
|
|
{
|
|
};
|
|
|
|
// The callable takes an argument of type Arg
|
|
template<typename F, typename Arg>
|
|
struct ResultTypeHelper<
|
|
F, Arg, typename std::enable_if_t<!std::is_invocable_v<std::decay_t<F>, QFuture<Arg>>>>
|
|
{
|
|
using ResultType = std::invoke_result_t<std::decay_t<F>, std::decay_t<Arg>>;
|
|
};
|
|
|
|
// The callable takes an argument of type QFuture<Arg>
|
|
template<class F, class Arg>
|
|
struct ResultTypeHelper<
|
|
F, Arg, typename std::enable_if_t<std::is_invocable_v<std::decay_t<F>, QFuture<Arg>>>>
|
|
{
|
|
using ResultType = std::invoke_result_t<std::decay_t<F>, QFuture<Arg>>;
|
|
};
|
|
|
|
// The callable takes an argument of type QFuture<void>
|
|
template<class F>
|
|
struct ResultTypeHelper<
|
|
F, void, typename std::enable_if_t<std::is_invocable_v<std::decay_t<F>, QFuture<void>>>>
|
|
{
|
|
using ResultType = std::invoke_result_t<std::decay_t<F>, QFuture<void>>;
|
|
};
|
|
|
|
// The callable doesn't take argument
|
|
template<class F>
|
|
struct ResultTypeHelper<
|
|
F, void, typename std::enable_if_t<!std::is_invocable_v<std::decay_t<F>, QFuture<void>>>>
|
|
{
|
|
using ResultType = std::invoke_result_t<std::decay_t<F>>;
|
|
};
|
|
|
|
// Helpers to remove QPrivateSignal argument from the list of arguments
|
|
|
|
template<class T, class Enable = void>
|
|
inline constexpr bool IsPrivateSignalArg = false;
|
|
|
|
template<class T>
|
|
inline constexpr bool IsPrivateSignalArg<T, typename std::enable_if_t<
|
|
// finds injected-class-name, the 'class' avoids falling into the rules of [class.qual]/2:
|
|
std::is_class_v<class T::QPrivateSignal>
|
|
>> = true;
|
|
|
|
template<class Tuple, std::size_t... I>
|
|
auto cutTuple(Tuple &&t, std::index_sequence<I...>)
|
|
{
|
|
return std::make_tuple(std::get<I>(t)...);
|
|
}
|
|
|
|
template<class Arg, class... Args>
|
|
auto createTuple(Arg &&arg, Args &&... args)
|
|
{
|
|
using TupleType = std::tuple<std::decay_t<Arg>, std::decay_t<Args>...>;
|
|
constexpr auto Size = sizeof...(Args); // One less than the size of all arguments
|
|
if constexpr (QtPrivate::IsPrivateSignalArg<std::tuple_element_t<Size, TupleType>>) {
|
|
if constexpr (Size == 1) {
|
|
return std::forward<Arg>(arg);
|
|
} else {
|
|
return cutTuple(std::make_tuple(std::forward<Arg>(arg), std::forward<Args>(args)...),
|
|
std::make_index_sequence<Size>());
|
|
}
|
|
} else {
|
|
return std::make_tuple(std::forward<Arg>(arg), std::forward<Args>(args)...);
|
|
}
|
|
}
|
|
|
|
// Helpers to resolve argument types of callables.
|
|
|
|
template<class Arg, class... Args>
|
|
using FilterLastPrivateSignalArg =
|
|
std::conditional_t<(sizeof...(Args) > 0),
|
|
std::invoke_result_t<decltype(createTuple<Arg, Args...>), Arg, Args...>,
|
|
std::conditional_t<IsPrivateSignalArg<Arg>, void, Arg>>;
|
|
|
|
template<typename...>
|
|
struct ArgsType;
|
|
|
|
template<typename Arg, typename... Args>
|
|
struct ArgsType<Arg, Args...>
|
|
{
|
|
using First = Arg;
|
|
using PromiseType = void;
|
|
using IsPromise = std::false_type;
|
|
static const bool HasExtraArgs = (sizeof...(Args) > 0);
|
|
using AllArgs = FilterLastPrivateSignalArg<std::decay_t<Arg>, std::decay_t<Args>...>;
|
|
|
|
template<class Class, class Callable>
|
|
static const bool CanInvokeWithArgs = std::is_invocable_v<Callable, Class, Arg, Args...>;
|
|
};
|
|
|
|
template<typename Arg, typename... Args>
|
|
struct ArgsType<QPromise<Arg> &, Args...>
|
|
{
|
|
using First = QPromise<Arg> &;
|
|
using PromiseType = Arg;
|
|
using IsPromise = std::true_type;
|
|
static const bool HasExtraArgs = (sizeof...(Args) > 0);
|
|
using AllArgs = FilterLastPrivateSignalArg<QPromise<Arg>, std::decay_t<Args>...>;
|
|
|
|
template<class Class, class Callable>
|
|
static const bool CanInvokeWithArgs = std::is_invocable_v<Callable, Class, QPromise<Arg> &, Args...>;
|
|
};
|
|
|
|
template<>
|
|
struct ArgsType<>
|
|
{
|
|
using First = void;
|
|
using PromiseType = void;
|
|
using IsPromise = std::false_type;
|
|
static const bool HasExtraArgs = false;
|
|
using AllArgs = void;
|
|
|
|
template<class Class, class Callable>
|
|
static const bool CanInvokeWithArgs = std::is_invocable_v<Callable, Class>;
|
|
};
|
|
|
|
template<typename F>
|
|
struct ArgResolver : ArgResolver<decltype(&std::decay_t<F>::operator())>
|
|
{
|
|
};
|
|
|
|
template<typename F>
|
|
struct ArgResolver<std::reference_wrapper<F>> : ArgResolver<decltype(&std::decay_t<F>::operator())>
|
|
{
|
|
};
|
|
|
|
template<typename R, typename... Args>
|
|
struct ArgResolver<R(Args...)> : public ArgsType<Args...>
|
|
{
|
|
};
|
|
|
|
template<typename R, typename... Args>
|
|
struct ArgResolver<R (*)(Args...)> : public ArgsType<Args...>
|
|
{
|
|
};
|
|
|
|
template<typename R, typename... Args>
|
|
struct ArgResolver<R (*&)(Args...)> : public ArgsType<Args...>
|
|
{
|
|
};
|
|
|
|
template<typename R, typename... Args>
|
|
struct ArgResolver<R (* const)(Args...)> : public ArgsType<Args...>
|
|
{
|
|
};
|
|
|
|
template<typename R, typename... Args>
|
|
struct ArgResolver<R (&)(Args...)> : public ArgsType<Args...>
|
|
{
|
|
};
|
|
|
|
template<typename Class, typename R, typename... Args>
|
|
struct ArgResolver<R (Class::*)(Args...)> : public ArgsType<Args...>
|
|
{
|
|
};
|
|
|
|
template<typename Class, typename R, typename... Args>
|
|
struct ArgResolver<R (Class::*)(Args...) noexcept> : public ArgsType<Args...>
|
|
{
|
|
};
|
|
|
|
template<typename Class, typename R, typename... Args>
|
|
struct ArgResolver<R (Class::*)(Args...) const> : public ArgsType<Args...>
|
|
{
|
|
};
|
|
|
|
template<typename Class, typename R, typename... Args>
|
|
struct ArgResolver<R (Class::*)(Args...) const noexcept> : public ArgsType<Args...>
|
|
{
|
|
};
|
|
|
|
template<typename Class, typename R, typename... Args>
|
|
struct ArgResolver<R (Class::* const)(Args...) const> : public ArgsType<Args...>
|
|
{
|
|
};
|
|
|
|
template<typename Class, typename R, typename... Args>
|
|
struct ArgResolver<R (Class::* const)(Args...) const noexcept> : public ArgsType<Args...>
|
|
{
|
|
};
|
|
|
|
template<class Class, class Callable>
|
|
using EnableIfInvocable = std::enable_if_t<
|
|
QtPrivate::ArgResolver<Callable>::template CanInvokeWithArgs<Class, Callable>>;
|
|
|
|
template<class T>
|
|
inline constexpr bool isQFutureV = false;
|
|
|
|
template<class T>
|
|
inline constexpr bool isQFutureV<QFuture<T>> = true;
|
|
|
|
template<class T>
|
|
using isQFuture = std::bool_constant<isQFutureV<T>>;
|
|
|
|
template<class T>
|
|
struct Future
|
|
{
|
|
};
|
|
|
|
template<class T>
|
|
struct Future<QFuture<T>>
|
|
{
|
|
using type = T;
|
|
};
|
|
|
|
template<class... Args>
|
|
using NotEmpty = std::bool_constant<(sizeof...(Args) > 0)>;
|
|
|
|
template<class Sequence>
|
|
using IsRandomAccessible =
|
|
std::is_convertible<typename std::iterator_traits<std::decay_t<decltype(
|
|
std::begin(std::declval<Sequence>()))>>::iterator_category,
|
|
std::random_access_iterator_tag>;
|
|
|
|
template<class Iterator>
|
|
using IsForwardIterable =
|
|
std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category,
|
|
std::forward_iterator_tag>;
|
|
|
|
template<typename Function, typename ResultType, typename ParentResultType>
|
|
class Continuation
|
|
{
|
|
public:
|
|
template<typename F = Function>
|
|
Continuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p)
|
|
: promise(std::move(p)), parentFuture(f), function(std::forward<F>(func))
|
|
{
|
|
}
|
|
virtual ~Continuation() = default;
|
|
|
|
bool execute();
|
|
|
|
template<typename F = Function>
|
|
static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &fi,
|
|
QtFuture::Launch policy);
|
|
|
|
template<typename F = Function>
|
|
static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &fi,
|
|
QThreadPool *pool);
|
|
|
|
template<typename F = Function>
|
|
static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &fi,
|
|
QObject *context);
|
|
|
|
private:
|
|
void fulfillPromiseWithResult();
|
|
void fulfillVoidPromise();
|
|
void fulfillPromiseWithVoidResult();
|
|
|
|
template<class... Args>
|
|
void fulfillPromise(Args &&... args);
|
|
|
|
protected:
|
|
virtual void runImpl() = 0;
|
|
|
|
void runFunction();
|
|
|
|
protected:
|
|
QPromise<ResultType> promise;
|
|
QFuture<ParentResultType> parentFuture;
|
|
Function function;
|
|
};
|
|
|
|
template<typename Function, typename ResultType, typename ParentResultType>
|
|
class SyncContinuation final : public Continuation<Function, ResultType, ParentResultType>
|
|
{
|
|
public:
|
|
template<typename F = Function>
|
|
SyncContinuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p)
|
|
: Continuation<Function, ResultType, ParentResultType>(std::forward<F>(func), f,
|
|
std::move(p))
|
|
{
|
|
}
|
|
|
|
~SyncContinuation() override = default;
|
|
|
|
private:
|
|
void runImpl() override { this->runFunction(); }
|
|
};
|
|
|
|
template<typename Function, typename ResultType, typename ParentResultType>
|
|
class AsyncContinuation final : public QRunnable,
|
|
public Continuation<Function, ResultType, ParentResultType>
|
|
{
|
|
public:
|
|
template<typename F = Function>
|
|
AsyncContinuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p,
|
|
QThreadPool *pool = nullptr)
|
|
: Continuation<Function, ResultType, ParentResultType>(std::forward<F>(func), f,
|
|
std::move(p)),
|
|
threadPool(pool)
|
|
{
|
|
}
|
|
|
|
~AsyncContinuation() override = default;
|
|
|
|
private:
|
|
void runImpl() override // from Continuation
|
|
{
|
|
QThreadPool *pool = threadPool ? threadPool : QThreadPool::globalInstance();
|
|
pool->start(this);
|
|
}
|
|
|
|
void run() override // from QRunnable
|
|
{
|
|
this->runFunction();
|
|
}
|
|
|
|
private:
|
|
QThreadPool *threadPool;
|
|
};
|
|
|
|
#ifndef QT_NO_EXCEPTIONS
|
|
|
|
template<class Function, class ResultType>
|
|
class FailureHandler
|
|
{
|
|
public:
|
|
template<typename F = Function>
|
|
static void create(F &&function, QFuture<ResultType> *future,
|
|
const QFutureInterface<ResultType> &fi);
|
|
|
|
template<typename F = Function>
|
|
static void create(F &&function, QFuture<ResultType> *future, QFutureInterface<ResultType> &fi,
|
|
QObject *context);
|
|
|
|
template<typename F = Function>
|
|
FailureHandler(F &&func, const QFuture<ResultType> &f, QPromise<ResultType> &&p)
|
|
: promise(std::move(p)), parentFuture(f), handler(std::forward<F>(func))
|
|
{
|
|
}
|
|
|
|
public:
|
|
void run();
|
|
|
|
private:
|
|
template<class ArgType>
|
|
void handleException();
|
|
void handleAllExceptions();
|
|
|
|
private:
|
|
QPromise<ResultType> promise;
|
|
QFuture<ResultType> parentFuture;
|
|
Function handler;
|
|
};
|
|
|
|
#endif
|
|
|
|
template<typename Function, typename ResultType, typename ParentResultType>
|
|
void Continuation<Function, ResultType, ParentResultType>::runFunction()
|
|
{
|
|
promise.start();
|
|
|
|
Q_ASSERT(parentFuture.isFinished());
|
|
|
|
#ifndef QT_NO_EXCEPTIONS
|
|
try {
|
|
#endif
|
|
if constexpr (!std::is_void_v<ResultType>) {
|
|
if constexpr (std::is_void_v<ParentResultType>) {
|
|
fulfillPromiseWithVoidResult();
|
|
} else if constexpr (std::is_invocable_v<Function, ParentResultType>) {
|
|
fulfillPromiseWithResult();
|
|
} else {
|
|
// This assert normally should never fail, this is to make sure
|
|
// that nothing unexpected happened.
|
|
static_assert(std::is_invocable_v<Function, QFuture<ParentResultType>>,
|
|
"The continuation is not invocable with the provided arguments");
|
|
fulfillPromise(parentFuture);
|
|
}
|
|
} else {
|
|
if constexpr (std::is_void_v<ParentResultType>) {
|
|
if constexpr (std::is_invocable_v<Function, QFuture<void>>)
|
|
function(parentFuture);
|
|
else
|
|
function();
|
|
} else if constexpr (std::is_invocable_v<Function, ParentResultType>) {
|
|
fulfillVoidPromise();
|
|
} else {
|
|
// This assert normally should never fail, this is to make sure
|
|
// that nothing unexpected happened.
|
|
static_assert(std::is_invocable_v<Function, QFuture<ParentResultType>>,
|
|
"The continuation is not invocable with the provided arguments");
|
|
function(parentFuture);
|
|
}
|
|
}
|
|
#ifndef QT_NO_EXCEPTIONS
|
|
} catch (...) {
|
|
promise.setException(std::current_exception());
|
|
}
|
|
#endif
|
|
promise.finish();
|
|
}
|
|
|
|
template<typename Function, typename ResultType, typename ParentResultType>
|
|
bool Continuation<Function, ResultType, ParentResultType>::execute()
|
|
{
|
|
Q_ASSERT(parentFuture.isFinished());
|
|
|
|
if (parentFuture.d.isChainCanceled()) {
|
|
#ifndef QT_NO_EXCEPTIONS
|
|
if (parentFuture.d.hasException()) {
|
|
// If the continuation doesn't take a QFuture argument, propagate the exception
|
|
// to the caller, by reporting it. If the continuation takes a QFuture argument,
|
|
// the user may want to catch the exception inside the continuation, to not
|
|
// interrupt the continuation chain, so don't report anything yet.
|
|
if constexpr (!std::is_invocable_v<std::decay_t<Function>, QFuture<ParentResultType>>) {
|
|
promise.start();
|
|
promise.setException(parentFuture.d.exceptionStore().exception());
|
|
promise.finish();
|
|
return false;
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
promise.start();
|
|
promise.future().cancel();
|
|
promise.finish();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
runImpl();
|
|
return true;
|
|
}
|
|
|
|
// Workaround for keeping move-only lambdas inside std::function
|
|
template<class Function>
|
|
struct ContinuationWrapper
|
|
{
|
|
ContinuationWrapper(Function &&f) : function(std::move(f)) { }
|
|
ContinuationWrapper(const ContinuationWrapper &other)
|
|
: function(std::move(const_cast<ContinuationWrapper &>(other).function))
|
|
{
|
|
Q_ASSERT_X(false, "QFuture", "Continuation shouldn't be copied");
|
|
}
|
|
ContinuationWrapper(ContinuationWrapper &&other) = default;
|
|
ContinuationWrapper &operator=(ContinuationWrapper &&) = default;
|
|
|
|
void operator()(const QFutureInterfaceBase &parentData) { function(parentData); }
|
|
|
|
private:
|
|
Function function;
|
|
};
|
|
|
|
template<typename Function, typename ResultType, typename ParentResultType>
|
|
template<typename F>
|
|
void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
|
|
QFuture<ParentResultType> *f,
|
|
QFutureInterface<ResultType> &fi,
|
|
QtFuture::Launch policy)
|
|
{
|
|
Q_ASSERT(f);
|
|
|
|
QThreadPool *pool = nullptr;
|
|
|
|
bool launchAsync = (policy == QtFuture::Launch::Async);
|
|
if (policy == QtFuture::Launch::Inherit) {
|
|
launchAsync = f->d.launchAsync();
|
|
|
|
// If the parent future was using a custom thread pool, inherit it as well.
|
|
if (launchAsync && f->d.threadPool()) {
|
|
pool = f->d.threadPool();
|
|
fi.setThreadPool(pool);
|
|
}
|
|
}
|
|
|
|
fi.setLaunchAsync(launchAsync);
|
|
|
|
auto continuation = [func = std::forward<F>(func), fi, promise = QPromise(fi), pool,
|
|
launchAsync](const QFutureInterfaceBase &parentData) mutable {
|
|
const auto parent = QFutureInterface<ParentResultType>(parentData).future();
|
|
Continuation<Function, ResultType, ParentResultType> *continuationJob = nullptr;
|
|
if (launchAsync) {
|
|
auto asyncJob = new AsyncContinuation<Function, ResultType, ParentResultType>(
|
|
std::forward<Function>(func), parent, std::move(promise), pool);
|
|
fi.setRunnable(asyncJob);
|
|
continuationJob = asyncJob;
|
|
} else {
|
|
continuationJob = new SyncContinuation<Function, ResultType, ParentResultType>(
|
|
std::forward<Function>(func), parent, std::move(promise));
|
|
}
|
|
|
|
bool isLaunched = continuationJob->execute();
|
|
// If continuation is successfully launched, AsyncContinuation will be deleted
|
|
// by the QThreadPool which has started it. Synchronous continuation will be
|
|
// executed immediately, so it's safe to always delete it here.
|
|
if (!(launchAsync && isLaunched)) {
|
|
delete continuationJob;
|
|
continuationJob = nullptr;
|
|
}
|
|
};
|
|
f->d.setContinuation(ContinuationWrapper(std::move(continuation)), fi.d);
|
|
}
|
|
|
|
template<typename Function, typename ResultType, typename ParentResultType>
|
|
template<typename F>
|
|
void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
|
|
QFuture<ParentResultType> *f,
|
|
QFutureInterface<ResultType> &fi,
|
|
QThreadPool *pool)
|
|
{
|
|
Q_ASSERT(f);
|
|
|
|
fi.setLaunchAsync(true);
|
|
fi.setThreadPool(pool);
|
|
|
|
auto continuation = [func = std::forward<F>(func), promise = QPromise(fi),
|
|
pool](const QFutureInterfaceBase &parentData) mutable {
|
|
const auto parent = QFutureInterface<ParentResultType>(parentData).future();
|
|
auto continuationJob = new AsyncContinuation<Function, ResultType, ParentResultType>(
|
|
std::forward<Function>(func), parent, std::move(promise), pool);
|
|
bool isLaunched = continuationJob->execute();
|
|
// If continuation is successfully launched, AsyncContinuation will be deleted
|
|
// by the QThreadPool which has started it.
|
|
if (!isLaunched) {
|
|
delete continuationJob;
|
|
continuationJob = nullptr;
|
|
}
|
|
};
|
|
f->d.setContinuation(ContinuationWrapper(std::move(continuation)), fi.d);
|
|
}
|
|
|
|
template<typename Function, typename ResultType, typename ParentResultType>
|
|
template<typename F>
|
|
void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
|
|
QFuture<ParentResultType> *f,
|
|
QFutureInterface<ResultType> &fi,
|
|
QObject *context)
|
|
{
|
|
Q_ASSERT(f);
|
|
|
|
auto continuation = [func = std::forward<F>(func), fi,
|
|
context = QPointer<QObject>(context)](
|
|
const QFutureInterfaceBase &parentData) mutable {
|
|
Q_ASSERT(context);
|
|
const auto parent = QFutureInterface<ParentResultType>(parentData).future();
|
|
QMetaObject::invokeMethod(
|
|
context,
|
|
[func = std::forward<F>(func), promise = QPromise(fi), parent]() mutable {
|
|
SyncContinuation<Function, ResultType, ParentResultType> continuationJob(
|
|
std::forward<Function>(func), parent, std::move(promise));
|
|
continuationJob.execute();
|
|
});
|
|
};
|
|
f->d.setContinuation(ContinuationWrapper(std::move(continuation)), fi.d);
|
|
}
|
|
|
|
template<typename Function, typename ResultType, typename ParentResultType>
|
|
void Continuation<Function, ResultType, ParentResultType>::fulfillPromiseWithResult()
|
|
{
|
|
if constexpr (std::is_copy_constructible_v<ParentResultType>)
|
|
fulfillPromise(parentFuture.result());
|
|
else
|
|
fulfillPromise(parentFuture.takeResult());
|
|
}
|
|
|
|
template<typename Function, typename ResultType, typename ParentResultType>
|
|
void Continuation<Function, ResultType, ParentResultType>::fulfillVoidPromise()
|
|
{
|
|
if constexpr (std::is_copy_constructible_v<ParentResultType>)
|
|
function(parentFuture.result());
|
|
else
|
|
function(parentFuture.takeResult());
|
|
}
|
|
|
|
template<typename Function, typename ResultType, typename ParentResultType>
|
|
void Continuation<Function, ResultType, ParentResultType>::fulfillPromiseWithVoidResult()
|
|
{
|
|
if constexpr (std::is_invocable_v<Function, QFuture<void>>)
|
|
fulfillPromise(parentFuture);
|
|
else
|
|
fulfillPromise();
|
|
}
|
|
|
|
template<typename Function, typename ResultType, typename ParentResultType>
|
|
template<class... Args>
|
|
void Continuation<Function, ResultType, ParentResultType>::fulfillPromise(Args &&... args)
|
|
{
|
|
promise.addResult(std::invoke(function, std::forward<Args>(args)...));
|
|
}
|
|
|
|
template<class T>
|
|
void fulfillPromise(QPromise<T> &promise, QFuture<T> &future)
|
|
{
|
|
if constexpr (!std::is_void_v<T>) {
|
|
if constexpr (std::is_copy_constructible_v<T>)
|
|
promise.addResult(future.result());
|
|
else
|
|
promise.addResult(future.takeResult());
|
|
}
|
|
}
|
|
|
|
template<class T, class Function>
|
|
void fulfillPromise(QPromise<T> &promise, Function &&handler)
|
|
{
|
|
if constexpr (std::is_void_v<T>)
|
|
handler();
|
|
else
|
|
promise.addResult(handler());
|
|
}
|
|
|
|
#ifndef QT_NO_EXCEPTIONS
|
|
|
|
template<class Function, class ResultType>
|
|
template<class F>
|
|
void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultType> *future,
|
|
const QFutureInterface<ResultType> &fi)
|
|
{
|
|
Q_ASSERT(future);
|
|
|
|
auto failureContinuation = [function = std::forward<F>(function), promise = QPromise(fi)](
|
|
const QFutureInterfaceBase &parentData) mutable {
|
|
const auto parent = QFutureInterface<ResultType>(parentData).future();
|
|
FailureHandler<Function, ResultType> failureHandler(std::forward<Function>(function),
|
|
parent, std::move(promise));
|
|
failureHandler.run();
|
|
};
|
|
|
|
future->d.setContinuation(ContinuationWrapper(std::move(failureContinuation)));
|
|
}
|
|
|
|
template<class Function, class ResultType>
|
|
template<class F>
|
|
void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultType> *future,
|
|
QFutureInterface<ResultType> &fi,
|
|
QObject *context)
|
|
{
|
|
Q_ASSERT(future);
|
|
|
|
auto failureContinuation =
|
|
[function = std::forward<F>(function), fi,
|
|
context = QPointer<QObject>(context)](const QFutureInterfaceBase &parentData) mutable {
|
|
Q_ASSERT(context);
|
|
const auto parent = QFutureInterface<ResultType>(parentData).future();
|
|
QMetaObject::invokeMethod(context,
|
|
[function = std::forward<F>(function),
|
|
promise = QPromise(fi), parent]() mutable {
|
|
FailureHandler<Function, ResultType> failureHandler(
|
|
std::forward<Function>(function), parent, std::move(promise));
|
|
failureHandler.run();
|
|
});
|
|
};
|
|
|
|
future->d.setContinuation(ContinuationWrapper(std::move(failureContinuation)));
|
|
}
|
|
|
|
template<class Function, class ResultType>
|
|
void FailureHandler<Function, ResultType>::run()
|
|
{
|
|
Q_ASSERT(parentFuture.isFinished());
|
|
|
|
promise.start();
|
|
|
|
if (parentFuture.d.hasException()) {
|
|
using ArgType = typename QtPrivate::ArgResolver<Function>::First;
|
|
if constexpr (std::is_void_v<ArgType>) {
|
|
handleAllExceptions();
|
|
} else {
|
|
handleException<ArgType>();
|
|
}
|
|
} else if (parentFuture.d.isChainCanceled()) {
|
|
promise.future().cancel();
|
|
} else {
|
|
QtPrivate::fulfillPromise(promise, parentFuture);
|
|
}
|
|
promise.finish();
|
|
}
|
|
|
|
template<class Function, class ResultType>
|
|
template<class ArgType>
|
|
void FailureHandler<Function, ResultType>::handleException()
|
|
{
|
|
try {
|
|
Q_ASSERT(parentFuture.d.hasException());
|
|
parentFuture.d.exceptionStore().rethrowException();
|
|
} catch (const ArgType &e) {
|
|
try {
|
|
// Handle exceptions matching with the handler's argument type
|
|
if constexpr (std::is_void_v<ResultType>)
|
|
handler(e);
|
|
else
|
|
promise.addResult(handler(e));
|
|
} catch (...) {
|
|
promise.setException(std::current_exception());
|
|
}
|
|
} catch (...) {
|
|
// Exception doesn't match with handler's argument type, propagate
|
|
// the exception to be handled later.
|
|
promise.setException(std::current_exception());
|
|
}
|
|
}
|
|
|
|
template<class Function, class ResultType>
|
|
void FailureHandler<Function, ResultType>::handleAllExceptions()
|
|
{
|
|
try {
|
|
Q_ASSERT(parentFuture.d.hasException());
|
|
parentFuture.d.exceptionStore().rethrowException();
|
|
} catch (...) {
|
|
try {
|
|
QtPrivate::fulfillPromise(promise, std::forward<Function>(handler));
|
|
} catch (...) {
|
|
promise.setException(std::current_exception());
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // QT_NO_EXCEPTIONS
|
|
|
|
template<class Function, class ResultType>
|
|
class CanceledHandler
|
|
{
|
|
public:
|
|
template<class F = Function>
|
|
static void create(F &&handler, QFuture<ResultType> *future, QFutureInterface<ResultType> &fi)
|
|
{
|
|
Q_ASSERT(future);
|
|
|
|
auto canceledContinuation = [promise = QPromise(fi), handler = std::forward<F>(handler)](
|
|
const QFutureInterfaceBase &parentData) mutable {
|
|
auto parentFuture = QFutureInterface<ResultType>(parentData).future();
|
|
run(std::forward<F>(handler), parentFuture, std::move(promise));
|
|
};
|
|
future->d.setContinuation(ContinuationWrapper(std::move(canceledContinuation)));
|
|
}
|
|
|
|
template<class F = Function>
|
|
static void create(F &&handler, QFuture<ResultType> *future, QFutureInterface<ResultType> &fi,
|
|
QObject *context)
|
|
{
|
|
Q_ASSERT(future);
|
|
auto canceledContinuation = [fi, handler = std::forward<F>(handler),
|
|
context = QPointer<QObject>(context)](
|
|
const QFutureInterfaceBase &parentData) mutable {
|
|
Q_ASSERT(context);
|
|
auto parentFuture = QFutureInterface<ResultType>(parentData).future();
|
|
QMetaObject::invokeMethod(context,
|
|
[promise = QPromise(fi), parentFuture,
|
|
handler = std::forward<F>(handler)]() mutable {
|
|
run(std::forward<F>(handler), parentFuture, std::move(promise));
|
|
});
|
|
};
|
|
|
|
future->d.setContinuation(ContinuationWrapper(std::move(canceledContinuation)));
|
|
}
|
|
|
|
template<class F = Function>
|
|
static void run(F &&handler, QFuture<ResultType> &parentFuture, QPromise<ResultType> &&promise)
|
|
{
|
|
promise.start();
|
|
|
|
if (parentFuture.isCanceled()) {
|
|
#ifndef QT_NO_EXCEPTIONS
|
|
if (parentFuture.d.hasException()) {
|
|
// Propagate the exception to the result future
|
|
promise.setException(parentFuture.d.exceptionStore().exception());
|
|
} else {
|
|
try {
|
|
#endif
|
|
QtPrivate::fulfillPromise(promise, std::forward<F>(handler));
|
|
#ifndef QT_NO_EXCEPTIONS
|
|
} catch (...) {
|
|
promise.setException(std::current_exception());
|
|
}
|
|
}
|
|
#endif
|
|
} else {
|
|
QtPrivate::fulfillPromise(promise, parentFuture);
|
|
}
|
|
|
|
promise.finish();
|
|
}
|
|
};
|
|
|
|
struct UnwrapHandler
|
|
{
|
|
template<class T>
|
|
static auto unwrapImpl(T *outer)
|
|
{
|
|
Q_ASSERT(outer);
|
|
|
|
using ResultType = typename QtPrivate::Future<std::decay_t<T>>::type;
|
|
using NestedType = typename QtPrivate::Future<ResultType>::type;
|
|
QFutureInterface<NestedType> promise(QFutureInterfaceBase::State::Pending);
|
|
|
|
outer->then([promise](const QFuture<ResultType> &outerFuture) mutable {
|
|
// We use the .then([](QFuture<ResultType> outerFuture) {...}) version
|
|
// (where outerFuture == *outer), to propagate the exception if the
|
|
// outer future has failed.
|
|
Q_ASSERT(outerFuture.isFinished());
|
|
#ifndef QT_NO_EXCEPTIONS
|
|
if (outerFuture.d.hasException()) {
|
|
promise.reportStarted();
|
|
promise.reportException(outerFuture.d.exceptionStore().exception());
|
|
promise.reportFinished();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
promise.reportStarted();
|
|
ResultType nestedFuture = outerFuture.result();
|
|
|
|
nestedFuture.then([promise] (const QFuture<NestedType> &nested) mutable {
|
|
#ifndef QT_NO_EXCEPTIONS
|
|
if (nested.d.hasException()) {
|
|
promise.reportException(nested.d.exceptionStore().exception());
|
|
} else
|
|
#endif
|
|
{
|
|
if constexpr (!std::is_void_v<NestedType>)
|
|
promise.reportResults(nested.results());
|
|
}
|
|
promise.reportFinished();
|
|
}).onCanceled([promise] () mutable {
|
|
promise.reportCanceled();
|
|
promise.reportFinished();
|
|
});
|
|
}).onCanceled([promise]() mutable {
|
|
// propagate the cancellation of the outer future
|
|
promise.reportStarted();
|
|
promise.reportCanceled();
|
|
promise.reportFinished();
|
|
});
|
|
return promise.future();
|
|
}
|
|
};
|
|
|
|
} // namespace QtPrivate
|
|
|
|
namespace QtFuture {
|
|
|
|
template<class Signal>
|
|
using ArgsType = typename QtPrivate::ArgResolver<Signal>::AllArgs;
|
|
|
|
template<class Sender, class Signal, typename = QtPrivate::EnableIfInvocable<Sender, Signal>>
|
|
static QFuture<ArgsType<Signal>> connect(Sender *sender, Signal signal)
|
|
{
|
|
using ArgsType = ArgsType<Signal>;
|
|
QFutureInterface<ArgsType> promise;
|
|
promise.reportStarted();
|
|
if (!sender) {
|
|
promise.reportCanceled();
|
|
promise.reportFinished();
|
|
return promise.future();
|
|
}
|
|
|
|
using Connections = std::pair<QMetaObject::Connection, QMetaObject::Connection>;
|
|
auto connections = std::make_shared<Connections>();
|
|
|
|
if constexpr (std::is_void_v<ArgsType>) {
|
|
connections->first =
|
|
QObject::connect(sender, signal, sender, [promise, connections]() mutable {
|
|
QObject::disconnect(connections->first);
|
|
QObject::disconnect(connections->second);
|
|
promise.reportFinished();
|
|
});
|
|
} else if constexpr (QtPrivate::ArgResolver<Signal>::HasExtraArgs) {
|
|
connections->first = QObject::connect(sender, signal, sender,
|
|
[promise, connections](auto... values) mutable {
|
|
QObject::disconnect(connections->first);
|
|
QObject::disconnect(connections->second);
|
|
promise.reportResult(QtPrivate::createTuple(
|
|
std::move(values)...));
|
|
promise.reportFinished();
|
|
});
|
|
} else {
|
|
connections->first = QObject::connect(sender, signal, sender,
|
|
[promise, connections](ArgsType value) mutable {
|
|
QObject::disconnect(connections->first);
|
|
QObject::disconnect(connections->second);
|
|
promise.reportResult(value);
|
|
promise.reportFinished();
|
|
});
|
|
}
|
|
|
|
if (!connections->first) {
|
|
promise.reportCanceled();
|
|
promise.reportFinished();
|
|
return promise.future();
|
|
}
|
|
|
|
connections->second =
|
|
QObject::connect(sender, &QObject::destroyed, sender, [promise, connections]() mutable {
|
|
QObject::disconnect(connections->first);
|
|
QObject::disconnect(connections->second);
|
|
promise.reportCanceled();
|
|
promise.reportFinished();
|
|
});
|
|
|
|
return promise.future();
|
|
}
|
|
|
|
template<typename T, typename = QtPrivate::EnableForNonVoid<T>>
|
|
static QFuture<std::decay_t<T>> makeReadyFuture(T &&value)
|
|
{
|
|
QFutureInterface<std::decay_t<T>> promise;
|
|
promise.reportStarted();
|
|
promise.reportResult(std::forward<T>(value));
|
|
promise.reportFinished();
|
|
|
|
return promise.future();
|
|
}
|
|
|
|
#if defined(Q_QDOC)
|
|
static QFuture<void> makeReadyFuture()
|
|
#else
|
|
template<typename T = void>
|
|
static QFuture<T> makeReadyFuture()
|
|
#endif
|
|
{
|
|
QFutureInterface<T> promise;
|
|
promise.reportStarted();
|
|
promise.reportFinished();
|
|
|
|
return promise.future();
|
|
}
|
|
|
|
template<typename T>
|
|
static QFuture<T> makeReadyFuture(const QList<T> &values)
|
|
{
|
|
QFutureInterface<T> promise;
|
|
promise.reportStarted();
|
|
promise.reportResults(values);
|
|
promise.reportFinished();
|
|
|
|
return promise.future();
|
|
}
|
|
|
|
#ifndef QT_NO_EXCEPTIONS
|
|
|
|
template<typename T = void>
|
|
static QFuture<T> makeExceptionalFuture(std::exception_ptr exception)
|
|
{
|
|
QFutureInterface<T> promise;
|
|
promise.reportStarted();
|
|
promise.reportException(exception);
|
|
promise.reportFinished();
|
|
|
|
return promise.future();
|
|
}
|
|
|
|
template<typename T = void>
|
|
static QFuture<T> makeExceptionalFuture(const QException &exception)
|
|
{
|
|
try {
|
|
exception.raise();
|
|
} catch (...) {
|
|
return makeExceptionalFuture<T>(std::current_exception());
|
|
}
|
|
Q_UNREACHABLE();
|
|
}
|
|
|
|
#endif // QT_NO_EXCEPTIONS
|
|
|
|
} // namespace QtFuture
|
|
|
|
namespace QtPrivate {
|
|
|
|
template<typename ResultFutures>
|
|
struct WhenAllContext
|
|
{
|
|
using ValueType = typename ResultFutures::value_type;
|
|
|
|
explicit WhenAllContext(qsizetype size) : remaining(size) {}
|
|
|
|
template<typename T = ValueType>
|
|
void checkForCompletion(qsizetype index, T &&future)
|
|
{
|
|
futures[index] = std::forward<T>(future);
|
|
const auto oldRemaining = remaining.fetchAndSubRelaxed(1);
|
|
Q_ASSERT(oldRemaining > 0);
|
|
if (oldRemaining <= 1) { // that was the last one
|
|
promise.addResult(futures);
|
|
promise.finish();
|
|
}
|
|
}
|
|
|
|
QAtomicInteger<qsizetype> remaining;
|
|
QPromise<ResultFutures> promise;
|
|
ResultFutures futures;
|
|
};
|
|
|
|
template<typename ResultType>
|
|
struct WhenAnyContext
|
|
{
|
|
using ValueType = ResultType;
|
|
|
|
template<typename T = ResultType, typename = EnableForNonVoid<T>>
|
|
void checkForCompletion(qsizetype, T &&result)
|
|
{
|
|
if (!ready.fetchAndStoreRelaxed(true)) {
|
|
promise.addResult(std::forward<T>(result));
|
|
promise.finish();
|
|
}
|
|
}
|
|
|
|
QAtomicInt ready = false;
|
|
QPromise<ResultType> promise;
|
|
};
|
|
|
|
template<qsizetype Index, typename ContextType, typename... Ts>
|
|
void addCompletionHandlersImpl(const std::shared_ptr<ContextType> &context,
|
|
const std::tuple<Ts...> &t)
|
|
{
|
|
auto future = std::get<Index>(t);
|
|
using ResultType = typename ContextType::ValueType;
|
|
future.then([context](const std::tuple_element_t<Index, std::tuple<Ts...>> &f) {
|
|
context->checkForCompletion(Index, ResultType { std::in_place_index<Index>, f });
|
|
}).onCanceled([context, future]() {
|
|
context->checkForCompletion(Index, ResultType { std::in_place_index<Index>, future });
|
|
});
|
|
|
|
if constexpr (Index != 0)
|
|
addCompletionHandlersImpl<Index - 1, ContextType, Ts...>(context, t);
|
|
}
|
|
|
|
template<typename ContextType, typename... Ts>
|
|
void addCompletionHandlers(const std::shared_ptr<ContextType> &context, const std::tuple<Ts...> &t)
|
|
{
|
|
constexpr qsizetype size = std::tuple_size<std::tuple<Ts...>>::value;
|
|
addCompletionHandlersImpl<size - 1, ContextType, Ts...>(context, t);
|
|
}
|
|
|
|
template<typename OutputSequence, typename InputIt, typename ValueType>
|
|
QFuture<OutputSequence> whenAllImpl(InputIt first, InputIt last)
|
|
{
|
|
const qsizetype size = std::distance(first, last);
|
|
if (size == 0)
|
|
return QtFuture::makeReadyFuture(OutputSequence());
|
|
|
|
const auto context = std::make_shared<QtPrivate::WhenAllContext<OutputSequence>>(size);
|
|
context->futures.resize(size);
|
|
context->promise.start();
|
|
|
|
qsizetype idx = 0;
|
|
for (auto it = first; it != last; ++it, ++idx) {
|
|
it->then([context, idx](const ValueType &f) {
|
|
context->checkForCompletion(idx, f);
|
|
}).onCanceled([context, idx, f = *it] {
|
|
context->checkForCompletion(idx, f);
|
|
});
|
|
}
|
|
return context->promise.future();
|
|
}
|
|
|
|
template<typename OutputSequence, typename... Futures>
|
|
QFuture<OutputSequence> whenAllImpl(Futures &&... futures)
|
|
{
|
|
constexpr qsizetype size = sizeof...(Futures);
|
|
const auto context = std::make_shared<QtPrivate::WhenAllContext<OutputSequence>>(size);
|
|
context->futures.resize(size);
|
|
context->promise.start();
|
|
|
|
QtPrivate::addCompletionHandlers(context, std::make_tuple(std::forward<Futures>(futures)...));
|
|
|
|
return context->promise.future();
|
|
}
|
|
|
|
template<typename InputIt, typename ValueType>
|
|
QFuture<QtFuture::WhenAnyResult<typename Future<ValueType>::type>> whenAnyImpl(InputIt first,
|
|
InputIt last)
|
|
{
|
|
using PackagedType = typename Future<ValueType>::type;
|
|
using ResultType = QtFuture::WhenAnyResult<PackagedType>;
|
|
|
|
const qsizetype size = std::distance(first, last);
|
|
if (size == 0) {
|
|
return QtFuture::makeReadyFuture(
|
|
QtFuture::WhenAnyResult { qsizetype(-1), QFuture<PackagedType>() });
|
|
}
|
|
|
|
const auto context = std::make_shared<QtPrivate::WhenAnyContext<ResultType>>();
|
|
context->promise.start();
|
|
|
|
qsizetype idx = 0;
|
|
for (auto it = first; it != last; ++it, ++idx) {
|
|
it->then([context, idx](const ValueType &f) {
|
|
context->checkForCompletion(idx, QtFuture::WhenAnyResult { idx, f });
|
|
}).onCanceled([context, idx, f = *it] {
|
|
context->checkForCompletion(idx, QtFuture::WhenAnyResult { idx, f });
|
|
});
|
|
}
|
|
return context->promise.future();
|
|
}
|
|
|
|
template<typename... Futures>
|
|
QFuture<std::variant<std::decay_t<Futures>...>> whenAnyImpl(Futures &&... futures)
|
|
{
|
|
using ResultType = std::variant<std::decay_t<Futures>...>;
|
|
|
|
const auto context = std::make_shared<QtPrivate::WhenAnyContext<ResultType>>();
|
|
context->promise.start();
|
|
|
|
QtPrivate::addCompletionHandlers(context, std::make_tuple(std::forward<Futures>(futures)...));
|
|
|
|
return context->promise.future();
|
|
}
|
|
|
|
} // namespace QtPrivate
|
|
|
|
QT_END_NAMESPACE
|