1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qthreadpooler_p.h"
41#include <QtCore/QDebug>
42
43QT_BEGIN_NAMESPACE
44
45namespace Qt3DCore {
46
47QThreadPooler::QThreadPooler(QObject *parent)
48 : QObject(parent)
49 , m_futureInterface(nullptr)
50 , m_mutex()
51 , m_taskCount(0)
52 , m_threadPool(QThreadPool::globalInstance())
53 , m_totalRunJobs(0)
54{
55 m_threadPool->setMaxThreadCount(QThreadPooler::maxThreadCount());
56 // Ensures that threads will never be recycled
57 m_threadPool->setExpiryTimeout(-1);
58}
59
60QThreadPooler::~QThreadPooler()
61{
62 // Wait till all tasks are finished before deleting mutex
63 QMutexLocker locker(&m_mutex);
64 locker.unlock();
65}
66
67void QThreadPooler::enqueueTasks(const QVector<RunnableInterface *> &tasks)
68{
69 // The caller have to set the mutex
70 const QVector<RunnableInterface *>::const_iterator end = tasks.cend();
71
72 m_totalRunJobs = 0;
73 for (QVector<RunnableInterface *>::const_iterator it = tasks.cbegin();
74 it != end; ++it) {
75
76 // Only AspectTaskRunnables are checked for dependencies.
77 static const auto hasDependencies = [](RunnableInterface *task) -> bool {
78 return (task->type() == RunnableInterface::RunnableType::AspectTask)
79 && (static_cast<AspectTaskRunnable *>(task)->m_dependerCount > 0);
80 };
81
82 if (!hasDependencies(*it) && !(*it)->reserved()) {
83 (*it)->setReserved(true);
84 if ((*it)->isRequired()) {
85 (*it)->setPooler(this);
86 m_threadPool->start(runnable: (*it));
87 } else {
88 skipTask(task: *it);
89 }
90 }
91 }
92}
93
94void QThreadPooler::skipTask(RunnableInterface *task)
95{
96 enqueueDepencies(task);
97
98 if (currentCount() == 0) {
99 if (m_futureInterface) {
100 m_futureInterface->reportFinished();
101 delete m_futureInterface;
102 }
103 m_futureInterface = nullptr;
104 }
105
106 delete task; // normally gets deleted by threadpool
107}
108
109void QThreadPooler::enqueueDepencies(RunnableInterface *task)
110{
111 release();
112
113 if (task->type() == RunnableInterface::RunnableType::AspectTask) {
114 AspectTaskRunnable *aspectTask = static_cast<AspectTaskRunnable *>(task);
115 const auto &dependers = aspectTask->m_dependers;
116 for (auto it = dependers.begin(); it != dependers.end(); ++it) {
117 AspectTaskRunnable *dependerTask = static_cast<AspectTaskRunnable *>(*it);
118 if (--dependerTask->m_dependerCount == 0) {
119 if (!dependerTask->reserved()) {
120 dependerTask->setReserved(true);
121 if ((*it)->isRequired()) {
122 dependerTask->setPooler(this);
123 m_threadPool->start(runnable: dependerTask);
124 } else {
125 skipTask(task: *it);
126 }
127 }
128 }
129 }
130 }
131}
132
133void QThreadPooler::taskFinished(RunnableInterface *task)
134{
135 const QMutexLocker locker(&m_mutex);
136
137 m_totalRunJobs++;
138
139 enqueueDepencies(task);
140
141 if (currentCount() == 0) {
142 if (m_futureInterface) {
143 m_futureInterface->reportFinished();
144 delete m_futureInterface;
145 }
146 m_futureInterface = nullptr;
147 }
148}
149
150QFuture<void> QThreadPooler::mapDependables(QVector<RunnableInterface *> &taskQueue)
151{
152 const QMutexLocker locker(&m_mutex);
153
154 if (!m_futureInterface)
155 m_futureInterface = new QFutureInterface<void>();
156 if (!taskQueue.empty())
157 m_futureInterface->reportStarted();
158
159 acquire(add: taskQueue.size());
160 enqueueTasks(tasks: taskQueue);
161
162 return QFuture<void>(m_futureInterface);
163}
164
165int QThreadPooler::waitForAllJobs()
166{
167 future().waitForFinished();
168 return m_totalRunJobs;
169}
170
171QFuture<void> QThreadPooler::future()
172{
173 const QMutexLocker locker(&m_mutex);
174
175 if (!m_futureInterface)
176 return QFuture<void>();
177 else
178 return QFuture<void>(m_futureInterface);
179}
180
181void QThreadPooler::acquire(int add)
182{
183 // The caller have to set the mutex
184
185 m_taskCount.fetchAndAddOrdered(valueToAdd: add);
186}
187
188void QThreadPooler::release()
189{
190 // The caller have to set the mutex
191
192 m_taskCount.fetchAndAddOrdered(valueToAdd: -1);
193}
194
195int QThreadPooler::currentCount() const
196{
197 // The caller have to set the mutex
198
199 return m_taskCount.loadRelaxed();
200}
201
202int QThreadPooler::maxThreadCount()
203{
204 static int threadCount = 0;
205
206 if (threadCount == 0) {
207 threadCount = QThread::idealThreadCount();
208 const QByteArray maxThreadCount = qgetenv(varName: "QT3D_MAX_THREAD_COUNT");
209 if (!maxThreadCount.isEmpty()) {
210 bool conversionOK = false;
211 const int maxThreadCountValue = maxThreadCount.toInt(ok: &conversionOK);
212 if (conversionOK)
213 threadCount = std::min(a: threadCount, b: maxThreadCountValue);
214 }
215 }
216
217 return threadCount;
218}
219
220} // namespace Qt3DCore
221
222QT_END_NAMESPACE
223

source code of qt3d/src/core/jobs/qthreadpooler.cpp