1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2010-04-13
7 * Description : Dynamically active thread
8 *
9 * Copyright (C) 2010-2012 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
10 *
11 * This program is free software; you can redistribute it
12 * and/or modify it under the terms of the GNU General
13 * Public License as published by the Free Software Foundation;
14 * either version 2, or (at your option)
15 * any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * ============================================================ */
23
24 #include "dynamicthread.h"
25
26 // Qt includes
27
28 #include <QMutex>
29 #include <QMutexLocker>
30 #include <QWaitCondition>
31
32 // Local includes
33
34 #include "digikam_debug.h"
35 #include "threadmanager.h"
36
37 namespace Digikam
38 {
39
40 class Q_DECL_HIDDEN DynamicThread::Private : public QRunnable
41 {
42 public:
43
Private(DynamicThread * const q)44 explicit Private(DynamicThread* const q)
45 : q (q),
46 assignedThread (nullptr),
47 running (true),
48 emitSignals (false),
49 inDestruction (false),
50 threadRequested (false),
51 state (DynamicThread::Inactive),
52 priority (QThread::InheritPriority),
53 previousPriority (QThread::InheritPriority)
54 {
55 setAutoDelete(false);
56 };
57
58 void run() override;
59
60 void takingThread();
61 bool transitionToRunning();
62 void transitionToInactive();
63
64 public:
65
66 DynamicThread* const q;
67 QThread* assignedThread;
68
69 volatile bool running;
70 volatile bool emitSignals;
71 volatile bool inDestruction;
72 volatile bool threadRequested;
73
74 volatile DynamicThread::State state;
75
76 QThread::Priority priority;
77 QThread::Priority previousPriority;
78
79 QMutex mutex;
80 QWaitCondition condVar;
81 };
82
takingThread()83 void DynamicThread::Private::takingThread()
84 {
85 QMutexLocker locker(&mutex);
86
87 // The thread we requested from the pool has now "arrived"
88
89 threadRequested = false;
90 }
91
transitionToRunning()92 bool DynamicThread::Private::transitionToRunning()
93 {
94 QMutexLocker locker(&mutex);
95
96 switch (state)
97 {
98 case DynamicThread::Scheduled:
99 {
100 // ensure that a newly scheduled thread does not run
101 // while an old, deactivated one has not yet called transitionToInactive
102
103 while (assignedThread)
104 {
105 condVar.wait(&mutex);
106 }
107
108 state = DynamicThread::Running;
109 running = true;
110 assignedThread = QThread::currentThread();
111 previousPriority = assignedThread->priority();
112
113 if (priority != QThread::InheritPriority)
114 {
115 assignedThread->setPriority(priority);
116 }
117
118 return true;
119 }
120
121 case DynamicThread::Deactivating:
122 {
123 return false;
124 }
125
126 case DynamicThread::Running:
127 {
128 qCDebug(DIGIKAM_GENERAL_LOG) << "Transition to Running: Invalid Running state" << q;
129 return false;
130 }
131
132 case DynamicThread::Inactive:
133 {
134 qCDebug(DIGIKAM_GENERAL_LOG) << "Transition to Running: Invalid Inactive state" << q;
135 return false;
136 }
137
138 default:
139 {
140 qCDebug(DIGIKAM_GENERAL_LOG) << "Transition to Running: Should never reach here: assert?" << q;
141 return false;
142 }
143 }
144 }
145
transitionToInactive()146 void DynamicThread::Private::transitionToInactive()
147 {
148 QMutexLocker locker(&mutex);
149
150 switch (state)
151 {
152 case DynamicThread::Scheduled:
153 case DynamicThread::Deactivating:
154 case DynamicThread::Running:
155 {
156 if (previousPriority != QThread::InheritPriority)
157 {
158 assignedThread->setPriority(previousPriority);
159 previousPriority = QThread::InheritPriority;
160 }
161
162 assignedThread = nullptr;
163
164 if (state != DynamicThread::Scheduled)
165 {
166 state = DynamicThread::Inactive;
167 }
168
169 condVar.wakeAll();
170 break;
171 }
172
173 case DynamicThread::Inactive:
174 {
175 qCDebug(DIGIKAM_GENERAL_LOG) << "Transition to Inactive: Invalid Inactive state" << q;
176 break;
177 }
178 }
179 }
180
run()181 void DynamicThread::Private::run()
182 {
183 if (emitSignals)
184 {
185 emit q->starting();
186 }
187
188 if (transitionToRunning())
189 {
190 takingThread();
191 q->run();
192 }
193 else
194 {
195 takingThread();
196 }
197
198 if (emitSignals)
199 {
200 emit q->finished();
201 }
202
203 transitionToInactive();
204
205 // as soon as we are inactive, we may get deleted!
206 }
207
208 // -----------------------------------------------------------------------------------------------
209
DynamicThread(QObject * const parent)210 DynamicThread::DynamicThread(QObject* const parent)
211 : QObject(parent),
212 d (new Private(this))
213 {
214 ThreadManager::instance()->initialize(this);
215 }
216
~DynamicThread()217 DynamicThread::~DynamicThread()
218 {
219 shutDown();
220 delete d;
221 }
222
shutDown()223 void DynamicThread::shutDown()
224 {
225 QMutexLocker locker(&d->mutex);
226 d->running = false;
227 d->inDestruction = true;
228 stop(locker);
229 wait(locker);
230 }
231
state() const232 DynamicThread::State DynamicThread::state() const
233 {
234 return d->state;
235 }
236
isRunning() const237 bool DynamicThread::isRunning() const
238 {
239 return ((d->state == Scheduled) || (d->state == Running) || (d->state == Deactivating));
240 }
241
threadMutex() const242 QMutex* DynamicThread::threadMutex() const
243 {
244 return &d->mutex;
245 }
246
isFinished() const247 bool DynamicThread::isFinished() const
248 {
249 return (d->state == Inactive);
250 }
251
setEmitSignals(bool emitThem)252 void DynamicThread::setEmitSignals(bool emitThem)
253 {
254 d->emitSignals = emitThem;
255 }
256
setPriority(QThread::Priority priority)257 void DynamicThread::setPriority(QThread::Priority priority)
258 {
259 if (d->priority == priority)
260 {
261 return;
262 }
263
264 d->priority = priority;
265
266 if (d->priority != QThread::InheritPriority)
267 {
268 QMutexLocker locker(&d->mutex);
269
270 if (d->assignedThread)
271 {
272 d->assignedThread->setPriority(d->priority);
273 }
274 }
275 }
276
priority() const277 QThread::Priority DynamicThread::priority() const
278 {
279 return d->priority;
280 }
281
start()282 void DynamicThread::start()
283 {
284 QMutexLocker locker(&d->mutex);
285 start(locker);
286 }
287
stop()288 void DynamicThread::stop()
289 {
290 QMutexLocker locker(&d->mutex);
291 stop(locker);
292 }
293
wait()294 void DynamicThread::wait()
295 {
296 QMutexLocker locker(&d->mutex);
297 wait(locker);
298 }
299
start(QMutexLocker & locker)300 void DynamicThread::start(QMutexLocker& locker)
301 {
302 if (d->inDestruction)
303 {
304 return;
305 }
306
307 switch (d->state)
308 {
309 case Inactive:
310 case Deactivating:
311 {
312 d->state = Scheduled;
313 break;
314 }
315
316 case Running:
317 case Scheduled:
318 {
319 return;
320 }
321 }
322
323 if (!d->threadRequested)
324 {
325 // avoid issuing multiple thread requests after very fast start/stop/start calls
326
327 d->threadRequested = true;
328
329 locker.unlock();
330 ThreadManager::instance()->schedule(d);
331 locker.relock();
332 }
333 }
334
stop(QMutexLocker & locker)335 void DynamicThread::stop(QMutexLocker& locker)
336 {
337 Q_UNUSED(locker);
338
339 switch (d->state)
340 {
341 case Scheduled:
342 case Running:
343 {
344 d->running = false;
345 d->state = Deactivating;
346 break;
347 }
348
349 case Inactive:
350 case Deactivating:
351 {
352 break;
353 }
354 }
355 }
356
wait(QMutexLocker & locker)357 void DynamicThread::wait(QMutexLocker& locker)
358 {
359 while (d->state != Inactive)
360 {
361 d->condVar.wait(locker.mutex());
362 }
363 }
364
runningFlag() const365 bool DynamicThread::runningFlag() const volatile
366 {
367 return d->running;
368 }
369
370 } // namespace Digikam
371