Rework QtPrivate::Continuation to get rid of virtual calls and rename it
The Continuation class is a template and polymorphic, which likely means that it instantiates a new type with a vtable for each combination of template parameters. Remove the Sync/Async continuation classes to get rid of the hierarcy. Instead, encode all the information about the type of the continuation into the class itself. This requires to add two new members: * QThreadPool (which was previously a member of an async class) * QRunnable (async class was previously derived from it). We cannot really eliminate the need for QThreadPool and QRunnable without a larger refactoring of the entire QFuture framework, because QFutureInterface requires a QRunnable in its implementation. However, this patch already allows us to get rid of all the virtual functions in the Continuation implementation. While on it, also remove the Function member and derive from QtPrivate::CompactStorage<Function> instead. This results in a significant reduce of a binary size for the user projects, specially if they have a wide usage of continuations. Taking tst_qfuture as an extreme example, the binary size on a release build on macOS is reduced by ~15% - from 3723576 to 3156344 bytes. The change seems to be BC, because all Continuation usages are from the inline code in other template classes, so most probably a binary linked with the older version of Qt will simply keep using the old implementation. Still, if the user project creates a shared library that utilizes continuations, and builds the library with the default visibility attributes, the private Continuation class gets exposed in the exported methods. To avoid ODR violations in such cases, simply rename the class to CompactContinuation. Fixes: QTBUG-124909 Change-Id: I8c8263dbaf407d922aed2017d659bbc6fd87083d Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
71a1f7c1ea
commit
699162c6fa
@ -280,7 +280,7 @@ private:
|
||||
friend class QFutureInterfaceBase;
|
||||
|
||||
template<class Function, class ResultType, class ParentResultType>
|
||||
friend class QtPrivate::Continuation;
|
||||
friend class QtPrivate::CompactContinuation;
|
||||
|
||||
template<class Function, class ResultType>
|
||||
friend class QtPrivate::CanceledHandler;
|
||||
@ -339,7 +339,7 @@ QFuture<typename QFuture<T>::template ResultType<Function>>
|
||||
QFuture<T>::then(QtFuture::Launch policy, Function &&function)
|
||||
{
|
||||
QFutureInterface<ResultType<Function>> promise(QFutureInterfaceBase::State::Pending);
|
||||
QtPrivate::Continuation<std::decay_t<Function>, ResultType<Function>, T>::create(
|
||||
QtPrivate::CompactContinuation<std::decay_t<Function>, ResultType<Function>, T>::create(
|
||||
std::forward<Function>(function), this, promise, policy);
|
||||
return promise.future();
|
||||
}
|
||||
@ -350,7 +350,7 @@ QFuture<typename QFuture<T>::template ResultType<Function>> QFuture<T>::then(QTh
|
||||
Function &&function)
|
||||
{
|
||||
QFutureInterface<ResultType<Function>> promise(QFutureInterfaceBase::State::Pending);
|
||||
QtPrivate::Continuation<std::decay_t<Function>, ResultType<Function>, T>::create(
|
||||
QtPrivate::CompactContinuation<std::decay_t<Function>, ResultType<Function>, T>::create(
|
||||
std::forward<Function>(function), this, promise, pool);
|
||||
return promise.future();
|
||||
}
|
||||
@ -361,7 +361,7 @@ QFuture<typename QFuture<T>::template ResultType<Function>> QFuture<T>::then(QOb
|
||||
Function &&function)
|
||||
{
|
||||
QFutureInterface<ResultType<Function>> promise(QFutureInterfaceBase::State::Pending);
|
||||
QtPrivate::Continuation<std::decay_t<Function>, ResultType<Function>, T>::create(
|
||||
QtPrivate::CompactContinuation<std::decay_t<Function>, ResultType<Function>, T>::create(
|
||||
std::forward<Function>(function), this, promise, context);
|
||||
return promise.future();
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#endif
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtCore/qfunctionaltools_impl.h>
|
||||
#include <QtCore/qfutureinterface.h>
|
||||
#include <QtCore/qthreadpool.h>
|
||||
#include <QtCore/qexception.h>
|
||||
@ -285,19 +286,37 @@ using IsForwardIterable =
|
||||
std::forward_iterator_tag>;
|
||||
|
||||
template<typename Function, typename ResultType, typename ParentResultType>
|
||||
class Continuation
|
||||
class CompactContinuation : private CompactStorage<Function>
|
||||
{
|
||||
Q_DISABLE_COPY_MOVE(Continuation)
|
||||
Q_DISABLE_COPY_MOVE(CompactContinuation)
|
||||
public:
|
||||
using Storage = CompactStorage<Function>;
|
||||
|
||||
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))
|
||||
CompactContinuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p)
|
||||
: Storage{std::forward<F>(func)}, promise(std::move(p)), parentFuture(f), type(Type::Sync)
|
||||
{
|
||||
}
|
||||
virtual ~Continuation() = default;
|
||||
|
||||
template<typename F = Function>
|
||||
CompactContinuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p,
|
||||
QThreadPool *pool)
|
||||
: Storage{std::forward<F>(func)}, promise(std::move(p)), parentFuture(f),
|
||||
threadPool(pool), type(Type::Async)
|
||||
{
|
||||
runObj = QRunnable::create([this] {
|
||||
this->runFunction();
|
||||
delete this;
|
||||
});
|
||||
runObj->setAutoDelete(false);
|
||||
}
|
||||
|
||||
~CompactContinuation() { delete runObj; }
|
||||
|
||||
bool execute();
|
||||
|
||||
QRunnable *runnable() const { return runObj; }
|
||||
|
||||
template<typename F = Function>
|
||||
static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &fi,
|
||||
QtFuture::Launch policy);
|
||||
@ -319,63 +338,30 @@ private:
|
||||
void fulfillPromise(Args &&... args);
|
||||
|
||||
protected:
|
||||
virtual void runImpl() = 0;
|
||||
void runImpl()
|
||||
{
|
||||
if (type == Type::Sync) {
|
||||
runFunction();
|
||||
} else {
|
||||
Q_ASSERT(runObj);
|
||||
QThreadPool *pool = threadPool ? threadPool : QThreadPool::globalInstance();
|
||||
pool->start(runObj);
|
||||
}
|
||||
}
|
||||
|
||||
void runFunction();
|
||||
|
||||
protected:
|
||||
enum class Type : quint8 {
|
||||
Sync,
|
||||
Async
|
||||
};
|
||||
|
||||
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;
|
||||
QThreadPool *threadPool = nullptr;
|
||||
QRunnable *runObj = nullptr;
|
||||
Type type;
|
||||
};
|
||||
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
@ -415,7 +401,7 @@ private:
|
||||
#endif
|
||||
|
||||
template<typename Function, typename ResultType, typename ParentResultType>
|
||||
void Continuation<Function, ResultType, ParentResultType>::runFunction()
|
||||
void CompactContinuation<Function, ResultType, ParentResultType>::runFunction()
|
||||
{
|
||||
promise.start();
|
||||
|
||||
@ -439,9 +425,9 @@ void Continuation<Function, ResultType, ParentResultType>::runFunction()
|
||||
} else {
|
||||
if constexpr (std::is_void_v<ParentResultType>) {
|
||||
if constexpr (std::is_invocable_v<Function, QFuture<void>>)
|
||||
function(parentFuture);
|
||||
this->object()(parentFuture);
|
||||
else
|
||||
function();
|
||||
this->object()();
|
||||
} else if constexpr (std::is_invocable_v<Function, ParentResultType>) {
|
||||
fulfillVoidPromise();
|
||||
} else {
|
||||
@ -449,7 +435,7 @@ void Continuation<Function, ResultType, ParentResultType>::runFunction()
|
||||
// that nothing unexpected happened.
|
||||
static_assert(std::is_invocable_v<Function, QFuture<ParentResultType>>,
|
||||
"The continuation is not invocable with the provided arguments");
|
||||
function(parentFuture);
|
||||
this->object()(parentFuture);
|
||||
}
|
||||
}
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
@ -461,7 +447,7 @@ void Continuation<Function, ResultType, ParentResultType>::runFunction()
|
||||
}
|
||||
|
||||
template<typename Function, typename ResultType, typename ParentResultType>
|
||||
bool Continuation<Function, ResultType, ParentResultType>::execute()
|
||||
bool CompactContinuation<Function, ResultType, ParentResultType>::execute()
|
||||
{
|
||||
Q_ASSERT(parentFuture.isFinished());
|
||||
|
||||
@ -513,7 +499,7 @@ private:
|
||||
|
||||
template<typename Function, typename ResultType, typename ParentResultType>
|
||||
template<typename F>
|
||||
void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
|
||||
void CompactContinuation<Function, ResultType, ParentResultType>::create(F &&func,
|
||||
QFuture<ParentResultType> *f,
|
||||
QFutureInterface<ResultType> &fi,
|
||||
QtFuture::Launch policy)
|
||||
@ -538,20 +524,20 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
|
||||
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;
|
||||
CompactContinuation<Function, ResultType, ParentResultType> *continuationJob = nullptr;
|
||||
if (launchAsync) {
|
||||
auto asyncJob = new AsyncContinuation<Function, ResultType, ParentResultType>(
|
||||
auto asyncJob = new CompactContinuation<Function, ResultType, ParentResultType>(
|
||||
std::forward<Function>(func), parent, std::move(promise_), pool);
|
||||
fi.setRunnable(asyncJob);
|
||||
fi.setRunnable(asyncJob->runnable());
|
||||
continuationJob = asyncJob;
|
||||
} else {
|
||||
continuationJob = new SyncContinuation<Function, ResultType, ParentResultType>(
|
||||
continuationJob = new CompactContinuation<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
|
||||
// from the QRunnable's lambda. Synchronous continuation will be
|
||||
// executed immediately, so it's safe to always delete it here.
|
||||
if (!(launchAsync && isLaunched)) {
|
||||
delete continuationJob;
|
||||
@ -563,7 +549,7 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
|
||||
|
||||
template<typename Function, typename ResultType, typename ParentResultType>
|
||||
template<typename F>
|
||||
void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
|
||||
void CompactContinuation<Function, ResultType, ParentResultType>::create(F &&func,
|
||||
QFuture<ParentResultType> *f,
|
||||
QFutureInterface<ResultType> &fi,
|
||||
QThreadPool *pool)
|
||||
@ -576,11 +562,11 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
|
||||
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>(
|
||||
auto continuationJob = new CompactContinuation<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.
|
||||
// from the QRunnable's lambda.
|
||||
if (!isLaunched) {
|
||||
delete continuationJob;
|
||||
continuationJob = nullptr;
|
||||
@ -600,7 +586,7 @@ void watchContinuation(const QObject *context, Continuation &&c, QFutureInterfac
|
||||
|
||||
template<typename Function, typename ResultType, typename ParentResultType>
|
||||
template<typename F>
|
||||
void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
|
||||
void CompactContinuation<Function, ResultType, ParentResultType>::create(F &&func,
|
||||
QFuture<ParentResultType> *f,
|
||||
QFutureInterface<ResultType> &fi,
|
||||
QObject *context)
|
||||
@ -613,7 +599,7 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
|
||||
// destroyed and, if it is not yet finished, cancelled.
|
||||
auto continuation = [func = std::forward<F>(func), parent = *f,
|
||||
promise_ = QPromise(fi)]() mutable {
|
||||
SyncContinuation<Function, ResultType, ParentResultType> continuationJob(
|
||||
CompactContinuation<Function, ResultType, ParentResultType> continuationJob(
|
||||
std::forward<Function>(func), parent, std::move(promise_));
|
||||
continuationJob.execute();
|
||||
};
|
||||
@ -622,7 +608,7 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
|
||||
}
|
||||
|
||||
template<typename Function, typename ResultType, typename ParentResultType>
|
||||
void Continuation<Function, ResultType, ParentResultType>::fulfillPromiseWithResult()
|
||||
void CompactContinuation<Function, ResultType, ParentResultType>::fulfillPromiseWithResult()
|
||||
{
|
||||
if constexpr (std::is_copy_constructible_v<ParentResultType>)
|
||||
fulfillPromise(parentFuture.result());
|
||||
@ -631,16 +617,16 @@ void Continuation<Function, ResultType, ParentResultType>::fulfillPromiseWithRes
|
||||
}
|
||||
|
||||
template<typename Function, typename ResultType, typename ParentResultType>
|
||||
void Continuation<Function, ResultType, ParentResultType>::fulfillVoidPromise()
|
||||
void CompactContinuation<Function, ResultType, ParentResultType>::fulfillVoidPromise()
|
||||
{
|
||||
if constexpr (std::is_copy_constructible_v<ParentResultType>)
|
||||
function(parentFuture.result());
|
||||
this->object()(parentFuture.result());
|
||||
else
|
||||
function(parentFuture.takeResult());
|
||||
this->object()(parentFuture.takeResult());
|
||||
}
|
||||
|
||||
template<typename Function, typename ResultType, typename ParentResultType>
|
||||
void Continuation<Function, ResultType, ParentResultType>::fulfillPromiseWithVoidResult()
|
||||
void CompactContinuation<Function, ResultType, ParentResultType>::fulfillPromiseWithVoidResult()
|
||||
{
|
||||
if constexpr (std::is_invocable_v<Function, QFuture<void>>)
|
||||
fulfillPromise(parentFuture);
|
||||
@ -650,9 +636,9 @@ void Continuation<Function, ResultType, ParentResultType>::fulfillPromiseWithVoi
|
||||
|
||||
template<typename Function, typename ResultType, typename ParentResultType>
|
||||
template<class... Args>
|
||||
void Continuation<Function, ResultType, ParentResultType>::fulfillPromise(Args &&... args)
|
||||
void CompactContinuation<Function, ResultType, ParentResultType>::fulfillPromise(Args &&... args)
|
||||
{
|
||||
promise.addResult(std::invoke(function, std::forward<Args>(args)...));
|
||||
promise.addResult(std::invoke(this->object(), std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
|
@ -28,7 +28,7 @@ class QFutureWatcherBasePrivate;
|
||||
|
||||
namespace QtPrivate {
|
||||
template<typename Function, typename ResultType, typename ParentResultType>
|
||||
class Continuation;
|
||||
class CompactContinuation;
|
||||
|
||||
class ExceptionStore;
|
||||
|
||||
@ -171,7 +171,7 @@ private:
|
||||
friend class QFutureWatcherBasePrivate;
|
||||
|
||||
template<typename Function, typename ResultType, typename ParentResultType>
|
||||
friend class QtPrivate::Continuation;
|
||||
friend class QtPrivate::CompactContinuation;
|
||||
|
||||
template<class Function, class ResultType>
|
||||
friend class QtPrivate::CanceledHandler;
|
||||
|
Loading…
x
Reference in New Issue
Block a user