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