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(QThreadPool *pool)
164 : futureInterface(nullptr), threadPool(pool)
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
222void ThreadEngineBase::reportIfSuspensionDone() const
223{
224 if (futureInterface && futureInterface->isSuspending())
225 futureInterface->reportSuspended();
226}
227
228bool ThreadEngineBase::isCanceled()
229{
230 if (futureInterface)
231 return futureInterface->isCanceled();
232 else
233 return false;
234}
235
236void ThreadEngineBase::waitForResume()
237{
238 if (futureInterface)
239 futureInterface->waitForResume();
240}
241
242bool ThreadEngineBase::isProgressReportingEnabled()
243{
244 // If we don't have a QFuture, there is no-one to report the progress to.
245 return (futureInterface != nullptr);
246}
247
248void ThreadEngineBase::setProgressValue(int progress)
249{
250 if (futureInterface)
251 futureInterface->setProgressValue(progress);
252}
253
254void ThreadEngineBase::setProgressRange(int minimum, int maximum)
255{
256 if (futureInterface)
257 futureInterface->setProgressRange(minimum, maximum);
258}
259
260bool ThreadEngineBase::startThreadInternal()
261{
262 if (this->isCanceled())
263 return false;
264
265 barrier.acquire();
266 if (!threadPool->tryStart(this)) {
267 barrier.release();
268 return false;
269 }
270 return true;
271}
272
273void ThreadEngineBase::startThreads()
274{
275 while (shouldStartThread() && startThreadInternal())
276 ;
277}
278
279void ThreadEngineBase::threadExit()
280{
281 const bool asynchronous = (futureInterface != nullptr);
282 const int lastThread = (barrier.release() == 0);
283
284 if (lastThread && asynchronous)
285 this->asynchronousFinish();
286}
287
288// Called by a worker thread that wants to be throttled. If the current number
289// of running threads is larger than one the thread is allowed to exit and
290// this function returns one.
291bool ThreadEngineBase::threadThrottleExit()
292{
293 return barrier.releaseUnlessLast();
294}
295
296void ThreadEngineBase::run() // implements QRunnable.
297{
298 if (this->isCanceled()) {
299 threadExit();
300 return;
301 }
302
303 startThreads();
304
305#ifndef QT_NO_EXCEPTIONS
306 try {
307#endif
308 while (threadFunction() == ThrottleThread) {
309 // threadFunction returning ThrottleThread means it that the user
310 // struct wants to be throttled by making a worker thread exit.
311 // Respect that request unless this is the only worker thread left
312 // running, in which case it has to keep going.
313 if (threadThrottleExit()) {
314 return;
315 } else {
316 // If the last worker thread is throttled and the state is "suspending",
317 // it means that suspension has been requested, and it is already
318 // in effect (because all previous threads have already exited).
319 // Report the "Suspended" state.
320 reportIfSuspensionDone();
321 }
322 }
323
324#ifndef QT_NO_EXCEPTIONS
325 } catch (QException &e) {
326 handleException(e);
327 } catch (...) {
328 handleException(QUnhandledException());
329 }
330#endif
331 threadExit();
332}
333
334#ifndef QT_NO_EXCEPTIONS
335
336void ThreadEngineBase::handleException(const QException &exception)
337{
338 if (futureInterface) {
339 futureInterface->reportException(exception);
340 } else {
341 QMutexLocker lock(&mutex);
342 if (!exceptionStore.hasException())
343 exceptionStore.setException(exception);
344 }
345}
346#endif
347
348
349} // namepsace QtConcurrent
350
351QT_END_NAMESPACE
352
353#endif // QT_NO_CONCURRENT
354