diff options
author | Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com> | 2015-02-23 13:47:13 +0100 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com> | 2015-02-23 13:47:13 +0100 |
commit | ca3f0d790de4918fe8df6cacaf9b4d3a46a649cf (patch) | |
tree | e28e9446fc06b07a91b71da89c209a591bcd62e1 /src/core | |
parent | b7f35a476caac0a0f56a04347b8d347c4eacd23c (diff) | |
parent | cb7753f3ffa85eed0fbd98cb2e4f0e453c595d6d (diff) |
Merge remote-tracking branch 'origin/dev' into 5.5
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/core.pro | 6 | ||||
-rw-r--r-- | src/core/jobs/dependencyhandler.cpp | 127 | ||||
-rw-r--r-- | src/core/jobs/dependencyhandler_p.h | 98 | ||||
-rw-r--r-- | src/core/jobs/jobrunner.cpp | 92 | ||||
-rw-r--r-- | src/core/jobs/jobrunner_p.h | 88 | ||||
-rw-r--r-- | src/core/jobs/jobs.pri | 24 | ||||
-rw-r--r-- | src/core/jobs/qaspectjobmanager.cpp | 78 | ||||
-rw-r--r-- | src/core/jobs/qaspectjobmanager_p.h | 12 | ||||
-rw-r--r-- | src/core/jobs/qthreadpooler.cpp | 260 | ||||
-rw-r--r-- | src/core/jobs/qthreadpooler_p.h | 86 | ||||
-rw-r--r-- | src/core/jobs/qthreadpooler_p_p.h | 93 | ||||
-rw-r--r-- | src/core/jobs/task.cpp | 142 | ||||
-rw-r--r-- | src/core/jobs/task_p.h | 123 |
13 files changed, 1222 insertions, 7 deletions
diff --git a/src/core/core.pro b/src/core/core.pro index cb9437700..bd965516e 100644 --- a/src/core/core.pro +++ b/src/core/core.pro @@ -6,6 +6,11 @@ load(qt_module) DEFINES += QT3DCORE_LIBRARY +# TODO: Make tasking API configurable +#use_thread_weaver { +# DEFINES += THREAD_WEAVER +#} + QMAKE_DOCS = $$PWD/doc/qt3dcore.qdocconf gcov { @@ -17,7 +22,6 @@ gcov { # otherwise mingw headers do not declare common functions like ::strcasecmp win32-g++*:QMAKE_CXXFLAGS_CXX11 = -std=gnu++0x -include(../3rdparty/threadweaver/src/threadweaver.pri) include(core.pri) !contains(QT_CONFIG, egl):DEFINES += QT_NO_EGL diff --git a/src/core/jobs/dependencyhandler.cpp b/src/core/jobs/dependencyhandler.cpp new file mode 100644 index 000000000..9778ede58 --- /dev/null +++ b/src/core/jobs/dependencyhandler.cpp @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <iterator> + +#include "dependencyhandler_p.h" + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +namespace { + template <template <typename T> class Op> + struct ByDepender { + typedef bool result_type; + + bool operator()(const QSharedPointer<TaskInterface> &lhs, const QSharedPointer<TaskInterface> &rhs) const Q_DECL_NOTHROW + { return Op<QSharedPointer<TaskInterface> >()(lhs, rhs); } + + bool operator()(const QSharedPointer<TaskInterface> &lhs, const Dependency &rhs) const Q_DECL_NOTHROW + { return operator()(lhs, rhs.depender); } + + bool operator()(const Dependency &lhs, const QSharedPointer<TaskInterface> &rhs) const Q_DECL_NOTHROW + { return operator()(lhs.depender, rhs); } + + bool operator()(const Dependency &lhs, const Dependency &rhs) const Q_DECL_NOTHROW + { return operator()(lhs.depender, rhs.depender); } + }; + + struct DependeeEquals : std::unary_function<Dependency, bool> + { + QSharedPointer<TaskInterface> dependee; + explicit DependeeEquals(QSharedPointer<TaskInterface> dependee) + : dependee(qMove(dependee)) {} + bool operator()(const Dependency &candidate) const + { + return dependee == candidate.dependee; + } + }; + + struct ByDependerThenDependee : std::binary_function<Dependency, Dependency, bool> + { + // Defines a lexicographical order (depender first). + bool operator()(const Dependency &lhs, const Dependency &rhs) + { + if (lhs.depender < rhs.depender) return true; + if (rhs.depender < lhs.depender) return false; + return lhs.dependee < rhs.dependee; + } + }; +} + +DependencyHandler::DependencyHandler() +{ +} + +void DependencyHandler::addDependencies(QVector<Dependency> dependencies) +{ + std::sort(dependencies.begin(), dependencies.end(), ByDependerThenDependee()); + + const QMutexLocker locker(&m_mutex); + + QVector<Dependency> newDependencyMap; + newDependencyMap.reserve(dependencies.size() + m_dependencyMap.size()); + std::set_union(m_dependencyMap.begin(), m_dependencyMap.end(), + dependencies.begin(), dependencies.end(), + std::back_inserter(newDependencyMap), ByDependerThenDependee()); + m_dependencyMap.swap(newDependencyMap); // commit +} + +bool DependencyHandler::hasDependency(const QSharedPointer<TaskInterface> &depender) +{ + const QMutexLocker locker(&m_mutex); + + return std::binary_search(m_dependencyMap.begin(), m_dependencyMap.end(), + depender, ByDepender<std::less>()); +} + +/* + * Removes all the entries on the m_dependencyMap that have given task as a dependee, + * i.e. entries where the dependency is on the given task. + */ +void DependencyHandler::freeDependencies(const QSharedPointer<TaskInterface> &dependee) +{ + const QMutexLocker locker(&m_mutex); + + m_dependencyMap.erase(std::remove_if(m_dependencyMap.begin(), m_dependencyMap.end(), + DependeeEquals(dependee)), + m_dependencyMap.end()); +} + +} // namespace Qt3D + +QT_END_NAMESPACE diff --git a/src/core/jobs/dependencyhandler_p.h b/src/core/jobs/dependencyhandler_p.h new file mode 100644 index 000000000..0d2aafaa6 --- /dev/null +++ b/src/core/jobs/dependencyhandler_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3D_DEPENDENCYHANDLER_P_H +#define QT3D_DEPENDENCYHANDLER_P_H + +#include "task_p.h" + +#include <QtCore/QMutex> +#include <QtCore/QVector> + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +struct Dependency +{ + Dependency() {} + Dependency(QSharedPointer<TaskInterface> depender, QSharedPointer<TaskInterface> dependee) + : depender(qMove(depender)), + dependee(qMove(dependee)) {} + + QSharedPointer<TaskInterface> depender; + QSharedPointer<TaskInterface> dependee; +}; + +} // namespace Qt3D + +template <> +class QTypeInfo<Qt3D::Dependency> : public QTypeInfoMerger<Qt3D::Dependency, QSharedPointer<Qt3D::TaskInterface> > {}; + +namespace Qt3D { + +inline bool operator==(const Dependency &left, const Dependency &right) +{ + return left.depender == right.depender && left.dependee == right.dependee; +} + +inline bool operator!=(const Dependency &left, const Dependency &right) +{ + return !operator==(left, right); +} + +class DependencyHandler +{ +public: + DependencyHandler(); + + void addDependencies(QVector<Dependency> dependencies); + bool hasDependency(const QSharedPointer<TaskInterface> &depender); + void freeDependencies(const QSharedPointer<TaskInterface> &dependee); + +private: + Q_DISABLE_COPY(DependencyHandler) + + QVector<Dependency> m_dependencyMap; + mutable QMutex m_mutex; +}; + +} // namespace Qt3D + +QT_END_NAMESPACE + +#endif // QT3D_DEPENDENCYHANDLER_P_H + diff --git a/src/core/jobs/jobrunner.cpp b/src/core/jobs/jobrunner.cpp new file mode 100644 index 000000000..d1cfe234b --- /dev/null +++ b/src/core/jobs/jobrunner.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "jobrunner_p.h" +#include "qthreadpooler_p.h" + +#include <QtCore/QThread> +#include <QtCore/QMutexLocker> +#include <QtCore/QAtomicInt> + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +JobRunner::JobRunner(QThreadPooler *parent) + : QThread(parent), + m_abort(0), + m_pooler(parent), + m_jobAvailable(Q_NULLPTR), + m_mutex(Q_NULLPTR) +{ + QObject::connect(parent, SIGNAL(shuttingDown()), this, SLOT(shutDown())); +} + +JobRunner::~JobRunner() +{ + shutDown(); +} + +void JobRunner::run() +{ + Q_ASSERT(m_jobAvailable != Q_NULLPTR); + + while (!m_abort.load()) { + if (const QSharedPointer<TaskInterface> task = m_pooler->nextTask()) { + m_pooler->startRunning(); + task->run(task, this); + m_pooler->stopRunning(); + } else { + suspend(); + } + } +} + +void JobRunner::suspend() +{ + const QMutexLocker locker(m_mutex); + + m_jobAvailable->wait(m_mutex); +} + +void JobRunner::shutDown() +{ + m_abort.store(1); +} + +} // namespace Qt3D + +QT_END_NAMESPACE diff --git a/src/core/jobs/jobrunner_p.h b/src/core/jobs/jobrunner_p.h new file mode 100644 index 000000000..14b5213e9 --- /dev/null +++ b/src/core/jobs/jobrunner_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3D_JOBRUNNER_P_H +#define QT3D_JOBRUNNER_P_H + +#include "qaspectjobmanager.h" +#include "task_p.h" + +#include <QtCore/QThread> +#include <QtCore/QWaitCondition> +#include <QtCore/QAtomicInt> + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +class QThreadPooler; + +class JobRunner : public QThread +{ + Q_OBJECT + +public: + explicit JobRunner(QThreadPooler *parent = 0); + ~JobRunner(); + + void run() Q_DECL_OVERRIDE; + + inline void setWaitConditions(QWaitCondition *jobAvailable) + { + m_jobAvailable = jobAvailable; + } + inline void setMutex(QMutex *mutex) { m_mutex = mutex; } + +private: + void suspend(); + +private: + QAtomicInt m_abort; + QThreadPooler *m_pooler; + + QWaitCondition *m_jobAvailable; + QMutex *m_mutex; // For waiting next available job + +private Q_SLOTS: + void shutDown(); +}; + +} // namespace Qt3D + +QT_END_NAMESPACE + +#endif // QT3D_JOBRUNNER_P_H + diff --git a/src/core/jobs/jobs.pri b/src/core/jobs/jobs.pri index 8c286bfe3..b6def7aa8 100644 --- a/src/core/jobs/jobs.pri +++ b/src/core/jobs/jobs.pri @@ -1,18 +1,34 @@ SOURCES += \ - $$PWD/weaverjob.cpp \ $$PWD/qaspectjob.cpp \ $$PWD/qaspectjobmanager.cpp \ - $$PWD/qabstractaspectjobmanager.cpp + $$PWD/qabstractaspectjobmanager.cpp \ + $$PWD/jobrunner.cpp \ + $$PWD/qthreadpooler.cpp \ + $$PWD/task.cpp \ + $$PWD/dependencyhandler.cpp + +# TODO: Make tasking API configurable +#use_thread_weaver { +#SOURCES += \ +# $$PWD/weaverjob.cpp +# +#HEADERS += \ +# $$PWD/weaverjob_p.h \ +#} HEADERS += \ - $$PWD/weaverjob_p.h \ $$PWD/qaspectjob.h \ $$PWD/qaspectjob_p.h \ $$PWD/qabstractaspectjobmanager.h \ $$PWD/qaspectjobproviderinterface.h \ $$PWD/qaspectjobmanager.h \ $$PWD/qaspectjobmanager_p.h \ - $$PWD/qabstractaspectjobmanager_p.h + $$PWD/qabstractaspectjobmanager_p.h \ + $$PWD/dependencyhandler_p.h \ + $$PWD/jobrunner_p.h \ + $$PWD/task_p.h \ + $$PWD/qthreadpooler_p_p.h \ + $$PWD/qthreadpooler_p.h INCLUDEPATH += $$PWD diff --git a/src/core/jobs/qaspectjobmanager.cpp b/src/core/jobs/qaspectjobmanager.cpp index cabe2a941..970c6458e 100644 --- a/src/core/jobs/qaspectjobmanager.cpp +++ b/src/core/jobs/qaspectjobmanager.cpp @@ -34,14 +34,18 @@ ** ****************************************************************************/ +#ifdef THREAD_WEAVER #include <threadweaver.h> #include <dependencypolicy.h> #include <thread.h> - -#include "qaspectjobmanager.h" #include "job.h" #include "weaverjob_p.h" +#endif + +#include "qaspectjobmanager.h" #include "qaspectjobmanager_p.h" +#include "task_p.h" +#include "dependencyhandler_p.h" #include <QAtomicInt> #include <QDebug> @@ -52,6 +56,7 @@ QT_BEGIN_NAMESPACE namespace Qt3D { +#ifdef THREAD_WEAVER namespace { class SynchronizedJob : public ThreadWeaver::Job @@ -92,11 +97,14 @@ void SynchronizedJob::run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread *t } } // anonymous +#endif QAspectJobManagerPrivate::QAspectJobManagerPrivate(QAspectJobManager *qq) : QAbstractAspectJobManagerPrivate() , q_ptr(qq) +#ifdef THREAD_WEAVER , m_weaver(Q_NULLPTR) +#endif { } @@ -104,16 +112,31 @@ QAspectJobManager::QAspectJobManager(QObject *parent) : QAbstractAspectJobManager(*new QAspectJobManagerPrivate(this), parent) { Q_D(QAspectJobManager); +#ifdef THREAD_WEAVER d->m_weaver = new ThreadWeaver::Queue(this); d->m_weaver->setMaximumNumberOfThreads(QThread::idealThreadCount()); +#else + d->m_threadPooler = new QThreadPooler(this); + d->m_threadPooler->setMaxThreadCount(QThread::idealThreadCount()); + + d->m_dependencyHandler = new DependencyHandler(); + d->m_threadPooler->setDependencyHandler(d->m_dependencyHandler); +#endif } QAspectJobManager::QAspectJobManager(QAspectJobManagerPrivate &dd, QObject *parent) : QAbstractAspectJobManager(dd, parent) { Q_D(QAspectJobManager); +#ifdef THREAD_WEAVER d->m_weaver = new ThreadWeaver::Queue(this); d->m_weaver->setMaximumNumberOfThreads(QThread::idealThreadCount()); +#else + d->m_threadPooler = new QThreadPooler(this); + d->m_threadPooler->setMaxThreadCount(QThread::idealThreadCount()); + + d->m_dependencyHandler = new DependencyHandler(); +#endif } void QAspectJobManager::initialize() @@ -123,6 +146,8 @@ void QAspectJobManager::initialize() void QAspectJobManager::enqueueJobs(const QVector<QAspectJobPtr> &jobQueue) { Q_D(QAspectJobManager); + +#ifdef THREAD_WEAVER // Convert QJobs to ThreadWeaver::Jobs QHash<QAspectJob *, QSharedPointer<WeaverJob> > jobsMap; Q_FOREACH (const QAspectJobPtr &job, jobQueue) { @@ -150,17 +175,55 @@ void QAspectJobManager::enqueueJobs(const QVector<QAspectJobPtr> &jobQueue) QSharedPointer<WeaverJob> weaverJob = jobsMap.value(job.data()); d->m_weaver->enqueue(weaverJob); } +#else + // Convert QJobs to Tasks + QHash<QAspectJob *, QSharedPointer<AspectTask>> tasksMap; + Q_FOREACH (const QAspectJobPtr &job, jobQueue) { + QSharedPointer<AspectTask> task = QSharedPointer<AspectTask>::create(); + task->m_job = job; + tasksMap.insert(job.data(), task); + } + + // Resolve dependencies + QVector<Dependency> dependencyList; + + Q_FOREACH (const QAspectJobPtr &job, jobQueue) { + const QVector<QWeakPointer<QAspectJob> > &deps = job->dependencies(); + + Q_FOREACH (const QWeakPointer<QAspectJob> &dep, deps) { + QSharedPointer<AspectTask> taskDependee = tasksMap.value(dep.data()); + + if (taskDependee) { + QSharedPointer<AspectTask> taskDepender = tasksMap.value(job.data()); + dependencyList.append(Dependency(taskDepender, taskDependee)); + taskDependee->setDependencyHandler(d->m_dependencyHandler); + } + } + } + d->m_dependencyHandler->addDependencies(qMove(dependencyList)); + + Q_FOREACH (const QAspectJobPtr &job, jobQueue) { + QSharedPointer<AspectTask> task = tasksMap.value(job.data()); + d->m_threadPooler->enqueueTask(task); + } +#endif } void QAspectJobManager::waitForAllJobs() { Q_D(QAspectJobManager); +#ifdef THREAD_WEAVER d->m_weaver->finish(); +#else + d->m_threadPooler->flush(); +#endif } void QAspectJobManager::waitForPerThreadFunction(JobFunction func, void *arg) { Q_D(QAspectJobManager); + +#ifdef THREAD_WEAVER const int threadCount = d->m_weaver->maximumNumberOfThreads(); QAtomicInt atomicCount(threadCount); @@ -170,6 +233,17 @@ void QAspectJobManager::waitForPerThreadFunction(JobFunction func, void *arg) } d->m_weaver->finish(); +#else + const int threadCount = d->m_threadPooler->maxThreadCount(); + QAtomicInt atomicCount(threadCount); + + for (int i = 0; i < threadCount; ++i) { + QSharedPointer<SynchronizedTask> syncTask(new SynchronizedTask(func, arg, &atomicCount)); + d->m_threadPooler->enqueueTask(syncTask); + } + + d->m_threadPooler->flush(); +#endif } } // namespace Qt3D diff --git a/src/core/jobs/qaspectjobmanager_p.h b/src/core/jobs/qaspectjobmanager_p.h index 9db148558..5ea9cc54a 100644 --- a/src/core/jobs/qaspectjobmanager_p.h +++ b/src/core/jobs/qaspectjobmanager_p.h @@ -39,9 +39,14 @@ #include <private/qabstractaspectjobmanager_p.h> +#include "qthreadpooler_p.h" +#include "dependencyhandler_p.h" + +#ifdef THREAD_WEAVER namespace ThreadWeaver { class Queue; } +#endif QT_BEGIN_NAMESPACE @@ -57,8 +62,15 @@ public: Q_DECLARE_PUBLIC(QAspectJobManager) QAspectJobManager *q_ptr; +#ifdef THREAD_WEAVER // Owned by QAspectJobManager via QObject parent-child ThreadWeaver::Queue *m_weaver; +#endif + + QThreadPooler *m_threadPooler; + DependencyHandler *m_dependencyHandler; + QMutex *m_syncMutex; + QWaitCondition m_syncFinished; }; } // Qt3D diff --git a/src/core/jobs/qthreadpooler.cpp b/src/core/jobs/qthreadpooler.cpp new file mode 100644 index 000000000..1620185b5 --- /dev/null +++ b/src/core/jobs/qthreadpooler.cpp @@ -0,0 +1,260 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qthreadpooler_p.h" +#include "qthreadpooler_p_p.h" +#include "jobrunner_p.h" +#include "dependencyhandler_p.h" +#include <QDebug> + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +QThreadPoolerPrivate::QThreadPoolerPrivate(QThreadPooler *qq) + : QObjectPrivate(), + m_mutex(new QMutex(QMutex::NonRecursive)), + m_runningThreads(0) +{ + q_ptr = qq; +} + +QThreadPoolerPrivate::~QThreadPoolerPrivate() +{ + Q_FOREACH (QSharedPointer<TaskInterface> task, m_taskQueue) + task->setDependencyHandler(Q_NULLPTR); + delete m_dependencyHandler; + + delete m_mutex; +} + +void QThreadPoolerPrivate::shutdown() +{ + m_jobFinished.wakeAll(); + + // When shutting down a signal is send for jobrunners to exit run() loop + // on next round. Sometimes the jobrunner is busy doing still the clean up + // tasks and isn't waiting the release of WaitCondition. Repeat the waking + // process max tryOuts. + const int tryOuts = 2; + + Q_FOREACH (JobRunner *jr, m_workers) { + if (!jr->isFinished()) { + for (int i = 0; i < tryOuts; i++) { + m_jobAvailable.wakeAll(); + if (jr->wait(100)) + break; + } + } + } +} + + +bool QThreadPoolerPrivate::isQueueEmpty() +{ + return m_taskQueue.isEmpty(); +} + +void QThreadPoolerPrivate::incRunningThreads() +{ + m_runningThreads += 1; +} + +void QThreadPoolerPrivate::decRunningThreads() +{ + m_runningThreads -= 1; + + // Sanity check + if (m_runningThreads < 0) + m_runningThreads = 0; +} + +void QThreadPoolerPrivate::createRunners(int threadCount) +{ + Q_Q(QThreadPooler); + + for (int i = 0; i < threadCount; i++) { + JobRunner *jr = new JobRunner(q); + + jr->setMutex(m_mutex); + jr->setWaitConditions(&(m_jobAvailable)); + jr->start(); + + m_workers.append(jr); + } +} + +int QThreadPoolerPrivate::maxThreadCount() const +{ + return m_maxThreadCount; +} + +void QThreadPoolerPrivate::setMaxThreadCount(int threadCount) +{ + + m_maxThreadCount = threadCount; + createRunners(m_maxThreadCount); +} + +///////////////////////////////////////////////// + +QThreadPooler::QThreadPooler(QObject *parent) + : QObject(*new QThreadPoolerPrivate(this), parent) +{ +} + +QThreadPooler::~QThreadPooler() +{ + Q_D(QThreadPooler); + + emit shuttingDown(); + d->m_jobAvailable.wakeAll(); + + d->shutdown(); +} + +int QThreadPooler::maxThreadCount() const +{ + Q_D(const QThreadPooler); + + const QMutexLocker locker(d->m_mutex); + + return d->maxThreadCount(); +} + +void QThreadPooler::setMaxThreadCount(int threadCount) +{ + Q_D(QThreadPooler); + + const QMutexLocker locker(d->m_mutex); + + d->setMaxThreadCount(threadCount); +} + +QSharedPointer<TaskInterface> QThreadPooler::nextTask() +{ + Q_D(QThreadPooler); + + const QMutexLocker locker(d->m_mutex); + + QSharedPointer<TaskInterface> task; + int queueSize = d->m_taskQueue.size(); + for (int i = 0; i < queueSize; i++) { + const QSharedPointer<TaskInterface> &candidate = d->m_taskQueue.at(i); + if (!hasDependencies(candidate)) { + task = candidate; + d->m_taskQueue.removeAt(i); + + break; + } + } + + return task; +} + +bool QThreadPooler::hasDependencies(const QSharedPointer<TaskInterface> &task) +{ + DependencyHandler *handler = task->dependencyHandler(); + if (handler) + return handler->hasDependency(task); + + return false; +} + +void QThreadPooler::enqueueTask(const QSharedPointer<TaskInterface> &task) +{ + Q_D(QThreadPooler); + + const QMutexLocker locker(d->m_mutex); + + d->m_taskQueue.append(task); + d->m_jobAvailable.wakeAll(); +} + +void QThreadPooler::flush() +{ + Q_D(QThreadPooler); + + const QMutexLocker locker(d->m_mutex); + +#ifdef QT_NO_DEBUG + const int waitTime = 50; +#else + const int waitTime = 500; +#endif + + while (!isIdling()) { + if (d->m_jobFinished.wait(d->m_mutex, waitTime) == false) + d->m_jobAvailable.wakeAll(); + } +} + +bool QThreadPooler::isIdling() +{ + Q_D(QThreadPooler); + + return d->isQueueEmpty() && d->m_runningThreads == 0; +} + +void QThreadPooler::startRunning() +{ + Q_D(QThreadPooler); + + const QMutexLocker locker(d->m_mutex); + + d->incRunningThreads(); +} + +void QThreadPooler::stopRunning() +{ + Q_D(QThreadPooler); + + const QMutexLocker locker(d->m_mutex); + + d->decRunningThreads(); + d->m_jobFinished.wakeAll(); +} + +void QThreadPooler::setDependencyHandler(DependencyHandler *handler) +{ + Q_D(QThreadPooler); + + d->setDependencyHandler(handler); +} + +} // namespace Qt3D + +QT_END_NAMESPACE diff --git a/src/core/jobs/qthreadpooler_p.h b/src/core/jobs/qthreadpooler_p.h new file mode 100644 index 000000000..6579e6283 --- /dev/null +++ b/src/core/jobs/qthreadpooler_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3D_QTHREADPOOLER_H +#define QT3D_QTHREADPOOLER_H + +#include "task_p.h" +#include "dependencyhandler_p.h" + +#include <QtCore/QObject> +#include <QtCore/QWaitCondition> +#include <QtCore/QSharedPointer> + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +class QThreadPoolerPrivate; + +class QThreadPooler : public QObject +{ + Q_OBJECT + +public: + explicit QThreadPooler(QObject *parent = 0); + ~QThreadPooler(); + + int maxThreadCount() const; + void setMaxThreadCount(int threadCount); + QSharedPointer<TaskInterface> nextTask(); + void enqueueTask(const QSharedPointer<TaskInterface> &task); + void flush(); + void startRunning(); + void stopRunning(); + void setDependencyHandler(DependencyHandler *handler); + +signals: + void shuttingDown(); + +private: + void manageThreads(); + bool hasDependencies(const QSharedPointer<TaskInterface> &task); + bool isIdling(); + +private: + Q_DECLARE_PRIVATE(QThreadPooler) +}; + +} // namespace Qt3D + +QT_END_NAMESPACE + +#endif // QT3D_QTHREADPOOLER_H diff --git a/src/core/jobs/qthreadpooler_p_p.h b/src/core/jobs/qthreadpooler_p_p.h new file mode 100644 index 000000000..0057acc36 --- /dev/null +++ b/src/core/jobs/qthreadpooler_p_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3D_QTHREADPOOLER_P_H +#define QT3D_QTHREADPOOLER_P_H + +#include "jobrunner_p.h" +#include "dependencyhandler_p.h" + +#include <QtCore/QtGlobal> +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +class QThreadPooler; + +class QThreadPoolerPrivate : public QObjectPrivate +{ +public: + QThreadPoolerPrivate(QThreadPooler *qq); + ~QThreadPoolerPrivate(); + + bool isQueueEmpty(); + + void incRunningThreads(); + void decRunningThreads(); + + inline void setDependencyHandler(DependencyHandler *handler) + { + m_dependencyHandler = handler; + } + + void createRunners(int threadCount); + void shutdown(); + + int maxThreadCount() const; + void setMaxThreadCount(int threadCount); + + Q_DECLARE_PUBLIC(QThreadPooler) + +private: + QList<JobRunner *> m_workers; + QVector<QSharedPointer<TaskInterface> > m_taskQueue; + + QWaitCondition m_jobAvailable; + QWaitCondition m_jobFinished; + QMutex *m_mutex; + int m_runningThreads; + int m_maxThreadCount; + DependencyHandler *m_dependencyHandler; +}; + +} + +QT_END_NAMESPACE + +#endif // QT3D_QTHREADPOOLER_P_H + diff --git a/src/core/jobs/task.cpp b/src/core/jobs/task.cpp new file mode 100644 index 000000000..87ddd57a5 --- /dev/null +++ b/src/core/jobs/task.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "task_p.h" +#include "jobrunner_p.h" +#include "dependencyhandler_p.h" + +#include <QMutexLocker> + +#include <QDebug> + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + + +TaskInterface::~TaskInterface() +{ +} + +// Aspect task + +AspectTask::AspectTask() + : m_dependencyHandler(0) +{ +} + +AspectTask::~AspectTask() +{ +} + +void AspectTask::run(QSharedPointer<TaskInterface> self, JobRunner *jr) +{ + Q_UNUSED(self); + Q_UNUSED(jr); + if (m_job) + m_job->run(); + + // Cleanup stuff + // For now at least dependecies. + if (m_dependencyHandler) + m_dependencyHandler->freeDependencies(self); +} + +void AspectTask::run() +{ +} + +void AspectTask::setDependencyHandler(DependencyHandler *handler) +{ + m_dependencyHandler = handler; +} + +DependencyHandler *AspectTask::dependencyHandler() +{ + return m_dependencyHandler; +} + +// Synchronized task + +SynchronizedTask::SynchronizedTask(QAbstractAspectJobManager::JobFunction func, + void *arg, QAtomicInt *atomicCount) + : m_func(func), + m_arg(arg), + m_atomicCount(atomicCount) +{ +} + +SynchronizedTask::~SynchronizedTask() +{ +} + +void SynchronizedTask::run() +{ + m_func(m_arg); +} + +void SynchronizedTask::run(QSharedPointer<TaskInterface> self, JobRunner *jr) +{ + Q_UNUSED(self); + Q_UNUSED(jr); + Q_ASSERT(m_func); + Q_ASSERT(jr); + + // Call the function + m_func(m_arg); + + // Decrement the atomic counter to let others know we've done our bit + m_atomicCount->deref(); + + // Wait for the other worker threads to be done + while (m_atomicCount->load() > 0) + jr->yieldCurrentThread(); +} + +void SynchronizedTask::setDependencyHandler(DependencyHandler *handler) +{ + Q_UNUSED(handler); +} + +DependencyHandler *SynchronizedTask::dependencyHandler() +{ + return Q_NULLPTR; +} + + +} // namespace Qt3D { + +QT_END_NAMESPACE diff --git a/src/core/jobs/task_p.h b/src/core/jobs/task_p.h new file mode 100644 index 000000000..cde8f90eb --- /dev/null +++ b/src/core/jobs/task_p.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3D_TASK_P_H +#define QT3D_TASK_P_H + +#include "qaspectjobmanager.h" + +#include <QtCore/QtGlobal> +#include <QtCore/QThread> +#include <QtCore/QSharedPointer> +#include <QtCore/QWaitCondition> + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +class JobRunner; +class DependencyHandler; + +class TaskInterface +{ +public: + virtual ~TaskInterface(); + + virtual void run(QSharedPointer<TaskInterface> self, JobRunner *jr) = 0; + virtual void run() = 0; + + virtual void setDependencyHandler(DependencyHandler *) = 0; + virtual DependencyHandler *dependencyHandler() = 0; + + virtual int id() = 0; + virtual void setId(int id) = 0; +}; + +class AspectTask : public TaskInterface +{ +public: + AspectTask(); + ~AspectTask(); + + int id() Q_DECL_OVERRIDE { return m_id; } + void setId(int id) Q_DECL_OVERRIDE { m_id = id; } + + void setDependencyHandler(DependencyHandler *handler) Q_DECL_OVERRIDE; + DependencyHandler *dependencyHandler() Q_DECL_OVERRIDE; + +public: + QSharedPointer<QAspectJob> m_job; + +protected: + void run(QSharedPointer<TaskInterface> self, JobRunner *jr) Q_DECL_OVERRIDE; + void run() Q_DECL_OVERRIDE; + +private: + DependencyHandler *m_dependencyHandler; + int m_id; // For testing purposes for now +}; + +class SynchronizedTask : public TaskInterface +{ +public: + explicit SynchronizedTask(QAbstractAspectJobManager::JobFunction func, void *arg, + QAtomicInt *atomicCount); + ~SynchronizedTask(); + + int id() Q_DECL_OVERRIDE { return m_id; } + void setId(int id) Q_DECL_OVERRIDE { m_id = id; } + + void setDependencyHandler(DependencyHandler *handler) Q_DECL_OVERRIDE; + DependencyHandler *dependencyHandler() Q_DECL_OVERRIDE; + +protected: + void run(QSharedPointer<TaskInterface> self, JobRunner *jr) Q_DECL_OVERRIDE; + void run() Q_DECL_OVERRIDE; + +private: + QAbstractAspectJobManager::JobFunction m_func; + void *m_arg; + QAtomicInt *m_atomicCount; + + int m_id; +}; + +} // namespace Qt3D + +QT_END_NAMESPACE + +#endif // QT3D_TASK_P_H + |