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
44 QT_BEGIN_NAMESPACE
45
46 namespace 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
ThreadEngineBarrier()88 ThreadEngineBarrier::ThreadEngineBarrier()
89 :count(0) { }
90
acquire()91 void ThreadEngineBarrier::acquire()
92 {
93 forever {
94 int localCount = count.loadRelaxed();
95 if (localCount < 0) {
96 if (count.testAndSetOrdered(localCount, localCount -1))
97 return;
98 } else {
99 if (count.testAndSetOrdered(localCount, localCount + 1))
100 return;
101 }
102 }
103 }
104
release()105 int ThreadEngineBarrier::release()
106 {
107 forever {
108 int localCount = count.loadRelaxed();
109 if (localCount == -1) {
110 if (count.testAndSetOrdered(-1, 0)) {
111 semaphore.release();
112 return 0;
113 }
114 } else if (localCount < 0) {
115 if (count.testAndSetOrdered(localCount, localCount + 1))
116 return qAbs(localCount + 1);
117 } else {
118 if (count.testAndSetOrdered(localCount, localCount - 1))
119 return localCount - 1;
120 }
121 }
122 }
123
124 // Wait until all threads have been released
wait()125 void 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(localCount, -localCount)) {
134 semaphore.acquire();
135 return;
136 }
137 }
138 }
139
currentCount()140 int 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.
releaseUnlessLast()147 bool ThreadEngineBarrier::releaseUnlessLast()
148 {
149 forever {
150 int localCount = count.loadRelaxed();
151 if (qAbs(localCount) == 1) {
152 return false;
153 } else if (localCount < 0) {
154 if (count.testAndSetOrdered(localCount, localCount + 1))
155 return true;
156 } else {
157 if (count.testAndSetOrdered(localCount, localCount - 1))
158 return true;
159 }
160 }
161 }
162
ThreadEngineBase()163 ThreadEngineBase::ThreadEngineBase()
164 :futureInterface(0), threadPool(QThreadPool::globalInstance())
165 {
166 setAutoDelete(false);
167 }
168
~ThreadEngineBase()169 ThreadEngineBase::~ThreadEngineBase() {}
170
startSingleThreaded()171 void ThreadEngineBase::startSingleThreaded()
172 {
173 start();
174 while (threadFunction() != ThreadFinished)
175 ;
176 finish();
177 }
178
startBlocking()179 void ThreadEngineBase::startBlocking()
180 {
181 start();
182 barrier.acquire();
183 startThreads();
184
185 bool throttled = false;
186 #ifndef QT_NO_EXCEPTIONS
187 try {
188 #endif
189 while (threadFunction() == ThrottleThread) {
190 if (threadThrottleExit()) {
191 throttled = true;
192 break;
193 }
194 }
195 #ifndef QT_NO_EXCEPTIONS
196 } catch (QException &e) {
197 handleException(e);
198 } catch (...) {
199 handleException(QUnhandledException());
200 }
201 #endif
202
203 if (throttled == false) {
204 barrier.release();
205 }
206
207 barrier.wait();
208 finish();
209 exceptionStore.throwPossibleException();
210 }
211
startThread()212 void ThreadEngineBase::startThread()
213 {
214 startThreadInternal();
215 }
216
acquireBarrierSemaphore()217 void ThreadEngineBase::acquireBarrierSemaphore()
218 {
219 barrier.acquire();
220 }
221
isCanceled()222 bool ThreadEngineBase::isCanceled()
223 {
224 if (futureInterface)
225 return futureInterface->isCanceled();
226 else
227 return false;
228 }
229
waitForResume()230 void ThreadEngineBase::waitForResume()
231 {
232 if (futureInterface)
233 futureInterface->waitForResume();
234 }
235
isProgressReportingEnabled()236 bool ThreadEngineBase::isProgressReportingEnabled()
237 {
238 // If we don't have a QFuture, there is no-one to report the progress to.
239 return (futureInterface != 0);
240 }
241
setProgressValue(int progress)242 void ThreadEngineBase::setProgressValue(int progress)
243 {
244 if (futureInterface)
245 futureInterface->setProgressValue(progress);
246 }
247
setProgressRange(int minimum,int maximum)248 void ThreadEngineBase::setProgressRange(int minimum, int maximum)
249 {
250 if (futureInterface)
251 futureInterface->setProgressRange(minimum, maximum);
252 }
253
startThreadInternal()254 bool ThreadEngineBase::startThreadInternal()
255 {
256 if (this->isCanceled())
257 return false;
258
259 barrier.acquire();
260 if (!threadPool->tryStart(this)) {
261 barrier.release();
262 return false;
263 }
264 return true;
265 }
266
startThreads()267 void ThreadEngineBase::startThreads()
268 {
269 while (shouldStartThread() && startThreadInternal())
270 ;
271 }
272
threadExit()273 void ThreadEngineBase::threadExit()
274 {
275 const bool asynchronous = futureInterface != 0;
276 const int lastThread = (barrier.release() == 0);
277
278 if (lastThread && asynchronous)
279 this->asynchronousFinish();
280 }
281
282 // Called by a worker thread that wants to be throttled. If the current number
283 // of running threads is larger than one the thread is allowed to exit and
284 // this function returns one.
threadThrottleExit()285 bool ThreadEngineBase::threadThrottleExit()
286 {
287 return barrier.releaseUnlessLast();
288 }
289
run()290 void ThreadEngineBase::run() // implements QRunnable.
291 {
292 if (this->isCanceled()) {
293 threadExit();
294 return;
295 }
296
297 startThreads();
298
299 #ifndef QT_NO_EXCEPTIONS
300 try {
301 #endif
302 while (threadFunction() == ThrottleThread) {
303 // threadFunction returning ThrottleThread means it that the user
304 // struct wants to be throttled by making a worker thread exit.
305 // Respect that request unless this is the only worker thread left
306 // running, in which case it has to keep going.
307 if (threadThrottleExit())
308 return;
309 }
310
311 #ifndef QT_NO_EXCEPTIONS
312 } catch (QException &e) {
313 handleException(e);
314 } catch (...) {
315 handleException(QUnhandledException());
316 }
317 #endif
318 threadExit();
319 }
320
321 #ifndef QT_NO_EXCEPTIONS
322
handleException(const QException & exception)323 void ThreadEngineBase::handleException(const QException &exception)
324 {
325 if (futureInterface)
326 futureInterface->reportException(exception);
327 else
328 exceptionStore.setException(exception);
329 }
330 #endif
331
332
333 } // namepsace QtConcurrent
334
335 QT_END_NAMESPACE
336
337 #endif // QT_NO_CONCURRENT
338