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(localCount, localCount -1))
97 return;
98 } else {
99 if (count.testAndSetOrdered(localCount, 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(-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
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(localCount, -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(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
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::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
212void ThreadEngineBase::startThread()
213{
214 startThreadInternal();
215}
216
217void ThreadEngineBase::acquireBarrierSemaphore()
218{
219 barrier.acquire();
220}
221
222bool ThreadEngineBase::isCanceled()
223{
224 if (futureInterface)
225 return futureInterface->isCanceled();
226 else
227 return false;
228}
229
230void ThreadEngineBase::waitForResume()
231{
232 if (futureInterface)
233 futureInterface->waitForResume();
234}
235
236bool 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
242void ThreadEngineBase::setProgressValue(int progress)
243{
244 if (futureInterface)
245 futureInterface->setProgressValue(progress);
246}
247
248void ThreadEngineBase::setProgressRange(int minimum, int maximum)
249{
250 if (futureInterface)
251 futureInterface->setProgressRange(minimum, maximum);
252}
253
254bool 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
267void ThreadEngineBase::startThreads()
268{
269 while (shouldStartThread() && startThreadInternal())
270 ;
271}
272
273void 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.
285bool ThreadEngineBase::threadThrottleExit()
286{
287 return barrier.releaseUnlessLast();
288}
289
290void 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
323void 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
335QT_END_NAMESPACE
336
337#endif // QT_NO_CONCURRENT
338