1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QTCONCURRENT_THREADENGINE_H
5#define QTCONCURRENT_THREADENGINE_H
6
7#include <QtConcurrent/qtconcurrent_global.h>
8
9#if !defined(QT_NO_CONCURRENT) ||defined(Q_QDOC)
10
11#include <QtCore/qthreadpool.h>
12#include <QtCore/qfuture.h>
13#include <QtCore/qdebug.h>
14#include <QtCore/qexception.h>
15#include <QtCore/qwaitcondition.h>
16#include <QtCore/qatomic.h>
17#include <QtCore/qsemaphore.h>
18
19QT_BEGIN_NAMESPACE
20
21
22namespace QtConcurrent {
23
24// The ThreadEngineBarrier counts worker threads, and allows one
25// thread to wait for all others to finish. Tested for its use in
26// QtConcurrent, requires more testing for use as a general class.
27class ThreadEngineBarrier
28{
29private:
30 // The thread count is maintained as an integer in the count atomic
31 // variable. The count can be either positive or negative - a negative
32 // count signals that a thread is waiting on the barrier.
33
34 QAtomicInt count;
35 QSemaphore semaphore;
36public:
37 ThreadEngineBarrier();
38 void acquire();
39 int release();
40 void wait();
41 int currentCount();
42 bool releaseUnlessLast();
43};
44
45enum ThreadFunctionResult { ThrottleThread, ThreadFinished };
46
47// The ThreadEngine controls the threads used in the computation.
48// Can be run in three modes: single threaded, multi-threaded blocking
49// and multi-threaded asynchronous.
50// The code for the single threaded mode is
51class Q_CONCURRENT_EXPORT ThreadEngineBase: public QRunnable
52{
53public:
54 // Public API:
55 ThreadEngineBase(QThreadPool *pool);
56 virtual ~ThreadEngineBase();
57 void startSingleThreaded();
58 void startThread();
59 bool isCanceled();
60 void waitForResume();
61 bool isProgressReportingEnabled();
62 void setProgressValue(int progress);
63 void setProgressRange(int minimum, int maximum);
64 void acquireBarrierSemaphore();
65 void reportIfSuspensionDone() const;
66
67protected: // The user overrides these:
68 virtual void start() {}
69 virtual void finish() {}
70 virtual ThreadFunctionResult threadFunction() { return ThreadFinished; }
71 virtual bool shouldStartThread() { return !shouldThrottleThread(); }
72 virtual bool shouldThrottleThread()
73 {
74 return futureInterface ? (futureInterface->isSuspending() || futureInterface->isSuspended())
75 : false;
76 }
77
78private:
79 bool startThreadInternal();
80 void startThreads();
81 void threadExit();
82 bool threadThrottleExit();
83 void run() override;
84 virtual void asynchronousFinish() = 0;
85#ifndef QT_NO_EXCEPTIONS
86 void handleException(const QException &exception);
87#endif
88protected:
89 QFutureInterfaceBase *futureInterface;
90 QThreadPool *threadPool;
91 ThreadEngineBarrier barrier;
92 QtPrivate::ExceptionStore exceptionStore;
93 QBasicMutex mutex;
94};
95
96
97template <typename T>
98class ThreadEngine : public ThreadEngineBase
99{
100public:
101 typedef T ResultType;
102
103 ThreadEngine(QThreadPool *pool) : ThreadEngineBase(pool) {}
104
105 virtual T *result() { return nullptr; }
106
107 QFutureInterface<T> *futureInterfaceTyped()
108 {
109 return static_cast<QFutureInterface<T> *>(futureInterface);
110 }
111
112 // Runs the user algorithm using a single thread.
113 T *startSingleThreaded()
114 {
115 ThreadEngineBase::startSingleThreaded();
116 return result();
117 }
118
119 // Runs the user algorithm using multiple threads.
120 // Does not block, returns a future.
121 QFuture<T> startAsynchronously()
122 {
123 futureInterface = new QFutureInterface<T>();
124
125 // reportStart() must be called before starting threads, otherwise the
126 // user algorithm might finish while reportStart() is running, which
127 // is very bad.
128 futureInterface->reportStarted();
129 QFuture<T> future = QFuture<T>(futureInterfaceTyped());
130 start();
131
132 acquireBarrierSemaphore();
133 threadPool->start(this);
134 return future;
135 }
136
137 void asynchronousFinish() override
138 {
139 finish();
140 futureInterfaceTyped()->reportFinished(result());
141 delete futureInterfaceTyped();
142 delete this;
143 }
144
145
146 void reportResult(const T *_result, int index = -1)
147 {
148 if (futureInterface)
149 futureInterfaceTyped()->reportResult(_result, index);
150 }
151
152 void reportResults(const QList<T> &_result, int index = -1, int count = -1)
153 {
154 if (futureInterface)
155 futureInterfaceTyped()->reportResults(_result, index, count);
156 }
157};
158
159// The ThreadEngineStarter class ecapsulates the return type
160// from the thread engine.
161// Depending on how the it is used, it will run
162// the engine in either blocking mode or asynchronous mode.
163template <typename T>
164class ThreadEngineStarterBase
165{
166public:
167 ThreadEngineStarterBase(ThreadEngine<T> *_threadEngine)
168 : threadEngine(_threadEngine) { }
169
170 inline ThreadEngineStarterBase(const ThreadEngineStarterBase &other)
171 : threadEngine(other.threadEngine) { }
172
173 QFuture<T> startAsynchronously()
174 {
175 return threadEngine->startAsynchronously();
176 }
177
178 operator QFuture<T>()
179 {
180 return startAsynchronously();
181 }
182
183protected:
184 ThreadEngine<T> *threadEngine;
185};
186
187
188// We need to factor out the code that dereferences the T pointer,
189// with a specialization where T is void. (code that dereferences a void *
190// won't compile)
191template <typename T>
192class ThreadEngineStarter : public ThreadEngineStarterBase<T>
193{
194 typedef ThreadEngineStarterBase<T> Base;
195 typedef ThreadEngine<T> TypedThreadEngine;
196public:
197 ThreadEngineStarter(TypedThreadEngine *eng)
198 : Base(eng) { }
199};
200
201// Full template specialization where T is void.
202template <>
203class ThreadEngineStarter<void> : public ThreadEngineStarterBase<void>
204{
205public:
206 ThreadEngineStarter(ThreadEngine<void> *_threadEngine)
207 : ThreadEngineStarterBase<void>(_threadEngine) {}
208};
209
210//! [qtconcurrentthreadengine-1]
211template <typename ThreadEngine>
212inline ThreadEngineStarter<typename ThreadEngine::ResultType> startThreadEngine(ThreadEngine *threadEngine)
213{
214 return ThreadEngineStarter<typename ThreadEngine::ResultType>(threadEngine);
215}
216
217} // namespace QtConcurrent
218
219
220QT_END_NAMESPACE
221
222#endif // QT_NO_CONCURRENT
223
224#endif
225

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