Create any callable using QRunnable::create

The overhead of making new custom classes appears to be less than
constructing a generic std::function.

[ChangeLog][QtCore][QRunnable] QRunnable::create can now take
non-copyable functions as argument.

Task-number: QTBUG-112302
Change-Id: Ied870f13ca6c7eaa14ed6eff9c4e676c7b73881c
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
This commit is contained in:
Allan Sandfeld Jensen 2023-03-28 10:38:25 +02:00
parent 5c4a94ba85
commit c080d1e64d
4 changed files with 91 additions and 20 deletions

View File

@ -509,6 +509,13 @@ QByteArray QMessageAuthenticationCode::hash(const QByteArray &msg, const QByteAr
qToByteArrayViewIgnoringNull(key), method); qToByteArrayViewIgnoringNull(key), method);
} }
#include "qrunnable.h"
QRunnable *QRunnable::create(std::function<void()> functionToRun)
{
return QRunnable::create<std::function<void()>>(std::move(functionToRun));
}
#include "qstring.h" #include "qstring.h"
qsizetype QString::toUcs4_helper(const ushort *uc, qsizetype length, uint *out) qsizetype QString::toUcs4_helper(const ushort *uc, qsizetype length, uint *out)

View File

@ -77,31 +77,17 @@ QRunnable::~QRunnable()
\sa autoDelete(), QThreadPool \sa autoDelete(), QThreadPool
*/ */
class FunctionRunnable : public QRunnable
{
std::function<void()> m_functionToRun;
public:
FunctionRunnable(std::function<void()> functionToRun) : m_functionToRun(std::move(functionToRun))
{
}
void run() override
{
m_functionToRun();
}
};
/*! /*!
\fn template<typename Callable> QRunnable *QRunnable::create(Callable &&callableToRun);
\since 5.15 \since 5.15
Creates a QRunnable that calls \a functionToRun in run(). Creates a QRunnable that calls \a callableToRun in run().
Auto-deletion is enabled by default. Auto-deletion is enabled by default.
\note In Qt versions prior to 6.6, this method took copyable functions only.
\sa run(), autoDelete() \sa run(), autoDelete()
*/ */
QRunnable *QRunnable::create(std::function<void()> functionToRun)
{
return new FunctionRunnable(std::move(functionToRun));
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -1,4 +1,4 @@
// Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2023 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 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QRUNNABLE_H #ifndef QRUNNABLE_H
@ -6,6 +6,7 @@
#include <QtCore/qglobal.h> #include <QtCore/qglobal.h>
#include <functional> #include <functional>
#include <type_traits>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -19,12 +20,70 @@ public:
constexpr QRunnable() noexcept = default; constexpr QRunnable() noexcept = default;
virtual ~QRunnable(); virtual ~QRunnable();
#if QT_CORE_REMOVED_SINCE(6, 6)
static QRunnable *create(std::function<void()> functionToRun); static QRunnable *create(std::function<void()> functionToRun);
#endif
template <typename Callable>
static QRunnable *create(Callable &&functionToRun);
bool autoDelete() const { return m_autoDelete; } bool autoDelete() const { return m_autoDelete; }
void setAutoDelete(bool autoDelete) { m_autoDelete = autoDelete; } void setAutoDelete(bool autoDelete) { m_autoDelete = autoDelete; }
protected:
// Type erasure, to only instantiate a non-virtual class per Callable:
class QGenericRunnableHelperBase
{
using OpFn = void(*)(const QGenericRunnableHelperBase *);
OpFn runFn;
OpFn destroyFn;
protected:
constexpr explicit QGenericRunnableHelperBase(OpFn fn, OpFn del) noexcept : runFn(fn), destroyFn(del) {}
~QGenericRunnableHelperBase() = default;
public:
void run() { runFn(this); }
void destroy() { destroyFn(this); }
};
template <typename Callable>
class QGenericRunnableHelper : public QGenericRunnableHelperBase
{
Callable m_functionToRun;
public:
template <typename UniCallable>
QGenericRunnableHelper(UniCallable &&functionToRun) noexcept :
QGenericRunnableHelperBase(
[](const QGenericRunnableHelperBase *that) { static_cast<const QGenericRunnableHelper*>(that)->m_functionToRun(); },
[](const QGenericRunnableHelperBase *that) { delete static_cast<const QGenericRunnableHelper*>(that); }),
m_functionToRun(std::forward<UniCallable>(functionToRun))
{
}
};
}; };
class QGenericRunnable : public QRunnable
{
QGenericRunnableHelperBase *runHelper;
public:
QGenericRunnable(QGenericRunnableHelperBase *runner) noexcept: runHelper(runner)
{
}
~QGenericRunnable() override
{
runHelper->destroy();
}
void run() override
{
runHelper->run();
}
};
template <typename Callable>
QRunnable *QRunnable::create(Callable &&functionToRun)
{
return new QGenericRunnable(
new QGenericRunnableHelper<std::decay_t<Callable>>(
std::forward<Callable>(functionToRun)));
}
QT_END_NAMESPACE QT_END_NAMESPACE
#endif #endif

View File

@ -6,6 +6,7 @@
#include <QSemaphore> #include <QSemaphore>
#include <qelapsedtimer.h> #include <qelapsedtimer.h>
#include <qrunnable.h>
#include <qthreadpool.h> #include <qthreadpool.h>
#include <qstring.h> #include <qstring.h>
#include <qmutex.h> #include <qmutex.h>
@ -45,6 +46,7 @@ public:
private slots: private slots:
void runFunction(); void runFunction();
void runFunction2(); void runFunction2();
void runFunction3();
void createThreadRunFunction(); void createThreadRunFunction();
void runMultiple(); void runMultiple();
void waitcomplete(); void waitcomplete();
@ -173,6 +175,23 @@ void tst_QThreadPool::runFunction2()
QCOMPARE(localCount, 1); QCOMPARE(localCount, 1);
} }
struct DeleteCheck
{
static bool s_deleted;
~DeleteCheck() { s_deleted = true; }
};
bool DeleteCheck::s_deleted = false;
void tst_QThreadPool::runFunction3()
{
std::unique_ptr<DeleteCheck> ptr(new DeleteCheck);
{
TestThreadPool manager;
manager.start(QRunnable::create([my_ptr = std::move(ptr)]() { }));
}
QVERIFY(DeleteCheck::s_deleted);
}
void tst_QThreadPool::createThreadRunFunction() void tst_QThreadPool::createThreadRunFunction()
{ {
{ {