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 QtConcurrent 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 "qtconcurrentthreadengine.h"
41
42#if !defined(QT_NO_CONCURRENT) || defined(Q_CLANG_QDOC)
43
44QT_BEGIN_NAMESPACE
45
46namespace QtConcurrent {
47
48/*!
49 \class QtConcurrent::ThreadEngineBarrier
50 \inmodule QtConcurrent
51 \internal
52*/
53
54/*!
55 \enum QtConcurrent::ThreadFunctionResult
56 \internal
57*/
58
59/*!
60 \class QtConcurrent::ThreadEngineBase
61 \inmodule QtConcurrent
62 \internal
63*/
64
65/*!
66 \class QtConcurrent::ThreadEngine
67 \inmodule QtConcurrent
68 \internal
69*/
70
71/*!
72 \class QtConcurrent::ThreadEngineStarterBase
73 \inmodule QtConcurrent
74 \internal
75*/
76
77/*!
78 \class QtConcurrent::ThreadEngineStarter
79 \inmodule QtConcurrent
80 \internal
81*/
82
83/*!
84 \fn [qtconcurrentthreadengine-1] template <typename ThreadEngine> ThreadEngineStarter<typename ThreadEngine::ResultType> QtConcurrent::startThreadEngine(ThreadEngine *threadEngine)
85 \internal
86*/
87
88ThreadEngineBarrier::ThreadEngineBarrier()
89:count(0) { }
90
91void ThreadEngineBarrier::acquire()
92{
93 forever {
94 int localCount = count.loadRelaxed();
95 if (localCount < 0) {
96 if (count.testAndSetOrdered(expectedValue: localCount, newValue: localCount -1))
97 return;
98 } else {
99 if (count.testAndSetOrdered(expectedValue: localCount, newValue: localCount + 1))
100 return;
101 }
102 }
103}
104
105int ThreadEngineBarrier::release()
106{
107 forever {
108 int localCount = count.loadRelaxed();
109 if (localCount == -1) {
110 if (count.testAndSetOrdered(expectedValue: -1, newValue: 0)) {
111 semaphore.release();
112 return 0;
113 }
114 } else if (localCount < 0) {
115 if (count.testAndSetOrdered(expectedValue: localCount, newValue: localCount + 1))
116 return qAbs(t: localCount + 1);
117 } else {
118 if (count.testAndSetOrdered(expectedValue: localCount, newValue: localCount - 1))
119 return localCount - 1;
120 }
121 }
122}
123
124// Wait until all threads have been released
125void ThreadEngineBarrier::wait()
126{
127 forever {
128 int localCount = count.loadRelaxed();
129 if (localCount == 0)
130 return;
131
132 Q_ASSERT(localCount > 0); // multiple waiters are not allowed.
133 if (count.testAndSetOrdered(expectedValue: localCount, newValue: -localCount)) {
134 semaphore.acquire();
135 return;
136 }
137 }
138}
139
140int ThreadEngineBarrier::currentCount()
141{
142 return count.loadRelaxed();
143}
144
145// releases a thread, unless this is the last thread.
146// returns true if the thread was released.
147bool ThreadEngineBarrier::releaseUnlessLast()
148{
149 forever {
150 int localCount = count.loadRelaxed();
151 if (qAbs(t: localCount) == 1) {
152 return false;
153 } else if (localCount < 0) {
154 if (count.testAndSetOrdered(expectedValue: localCount, newValue: localCount + 1))
155 return true;
156 } else {
157 if (count.testAndSetOrdered(expectedValue: localCount, newValue: localCount - 1))
158 return true;
159 }
160 }
161}
162
163ThreadEngineBase::ThreadEngineBase()
164:futureInterface(0), threadPool(QThreadPool::globalInstance())
165{
166 setAutoDelete(false);
167}
168
169ThreadEngineBase::~ThreadEngineBase() {}
170
171void ThreadEngineBase::startSingleThreaded()
172{
173 start();
174 while (threadFunction() != ThreadFinished)
175 ;
176 finish();
177}
178
179void ThreadEngineBase::startThread()
180{
181 startThreadInternal();
182}
183
184void ThreadEngineBase::acquireBarrierSemaphore()
185{
186 barrier.acquire();
187}
188
189bool ThreadEngineBase::isCanceled()
190{
191 if (futureInterface)
192 return futureInterface->isCanceled();
193 else
194 return false;
195}
196
197void ThreadEngineBase::waitForResume()
198{
199 if (futureInterface)
200 futureInterface->waitForResume();
201}
202
203bool ThreadEngineBase::isProgressReportingEnabled()
204{
205 // If we don't have a QFuture, there is no-one to report the progress to.
206 return (futureInterface != 0);
207}
208
209void ThreadEngineBase::setProgressValue(int progress)
210{
211 if (futureInterface)
212 futureInterface->setProgressValue(progress);
213}
214
215void ThreadEngineBase::setProgressRange(int minimum, int maximum)
216{
217 if (futureInterface)
218 futureInterface->setProgressRange(minimum, maximum);
219}
220
221bool ThreadEngineBase::startThreadInternal()
222{
223 if (this->isCanceled())
224 return false;
225
226 barrier.acquire();
227 if (!threadPool->tryStart(runnable: this)) {
228 barrier.release();
229 return false;
230 }
231 return true;
232}
233
234void ThreadEngineBase::startThreads()
235{
236 while (shouldStartThread() && startThreadInternal())
237 ;
238}
239
240void ThreadEngineBase::threadExit()
241{
242 const bool asynchronous = futureInterface != 0;
243 const int lastThread = (barrier.release() == 0);
244
245 if (lastThread && asynchronous)
246 this->asynchronousFinish();
247}
248
249// Called by a worker thread that wants to be throttled. If the current number
250// of running threads is larger than one the thread is allowed to exit and
251// this function returns one.
252bool ThreadEngineBase::threadThrottleExit()
253{
254 return barrier.releaseUnlessLast();
255}
256
257void ThreadEngineBase::run() // implements QRunnable.
258{
259 if (this->isCanceled()) {
260 threadExit();
261 return;
262 }
263
264 startThreads();
265
266#ifndef QT_NO_EXCEPTIONS
267 try {
268#endif
269 while (threadFunction() == ThrottleThread) {
270 // threadFunction returning ThrottleThread means it that the user
271 // struct wants to be throttled by making a worker thread exit.
272 // Respect that request unless this is the only worker thread left
273 // running, in which case it has to keep going.
274 if (threadThrottleExit())
275 return;
276 }
277
278#ifndef QT_NO_EXCEPTIONS
279 } catch (QException &e) {
280 handleException(exception: e);
281 } catch (...) {
282 handleException(exception: QUnhandledException());
283 }
284#endif
285 threadExit();
286}
287
288#ifndef QT_NO_EXCEPTIONS
289
290void ThreadEngineBase::handleException(const QException &exception)
291{
292 if (futureInterface)
293 futureInterface->reportException(e: exception);
294 else
295 exceptionStore.setException(exception);
296}
297#endif
298
299
300} // namepsace QtConcurrent
301
302QT_END_NAMESPACE
303
304#endif // QT_NO_CONCURRENT
305

source code of qtbase/src/concurrent/qtconcurrentthreadengine.cpp