File indexing completed on 2025-03-09 03:56:12
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2010-04-13 0007 * Description : Thread object scheduling 0008 * 0009 * SPDX-FileCopyrightText: 2010-2012 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "threadmanager.h" 0016 0017 // Qt includes 0018 0019 #include <QEventLoop> 0020 #include <QMutex> 0021 #include <QMutexLocker> 0022 #include <QPair> 0023 #include <QThread> 0024 #include <QThreadPool> 0025 #include <QWaitCondition> 0026 0027 // Local includes 0028 0029 #include "digikam_debug.h" 0030 #include "dynamicthread.h" 0031 #include "workerobject.h" 0032 0033 namespace Digikam 0034 { 0035 0036 class Q_DECL_HIDDEN ParkingThread : public QThread 0037 { 0038 Q_OBJECT 0039 0040 public: 0041 0042 explicit ParkingThread(QObject* const parent = nullptr) 0043 : QThread(parent), 0044 running(true) 0045 { 0046 start(); 0047 } 0048 0049 ~ParkingThread() override 0050 { 0051 running = false; 0052 condVar.wakeAll(); 0053 wait(); 0054 } 0055 0056 void parkObject(QObject* const object) 0057 { 0058 object->moveToThread(this); 0059 QMutexLocker locker(&mutex); 0060 condVar.wakeAll(); 0061 } 0062 0063 void moveToCurrentThread(QObject* const parkedObject) 0064 { 0065 if (parkedObject->thread() == QThread::currentThread()) 0066 { 0067 return; 0068 } 0069 0070 // We have 1. The current thread 0071 // 2. ParkingThread's thread 0072 // 3. The parkedObject's thread, subject to change. 0073 0074 QMutexLocker locker(&mutex); 0075 0076 // first, wait until the object has been parked in ParkingThread by its owning thread. 0077 0078 while (parkedObject->thread() != this) 0079 { 0080 condVar.wait(&mutex); 0081 } 0082 0083 QThread* const targetThread = QThread::currentThread(); 0084 0085 // then, now that it's parked in ParkingThread, make ParkingThread move it to the current thread. 0086 0087 todo << qMakePair(parkedObject, targetThread); 0088 condVar.wakeAll(); 0089 0090 // wait until ParkingThread has pushed the object to current thread 0091 0092 while (parkedObject->thread() != targetThread) 0093 { 0094 condVar.wait(&mutex); 0095 } 0096 } 0097 0098 void run() override 0099 { 0100 /* The quirk here is that this thread never runs an event loop. 0101 * That means events queued for parked object are only emitted when 0102 * these object have been moved to their own thread. 0103 */ 0104 while (running) 0105 { 0106 QList<TodoPair> copyTodo; 0107 { 0108 QMutexLocker locker(&mutex); 0109 condVar.wakeAll(); 0110 0111 if (todo.isEmpty()) 0112 { 0113 condVar.wait(&mutex); 0114 continue; 0115 } 0116 else 0117 { 0118 copyTodo = todo; 0119 todo.clear(); 0120 } 0121 } 0122 0123 Q_FOREACH (const TodoPair& pair, copyTodo) 0124 { 0125 pair.first->moveToThread(pair.second); 0126 } 0127 } 0128 } 0129 0130 public: 0131 0132 volatile bool running; 0133 typedef QPair<QObject*, QThread*> TodoPair; 0134 QMutex mutex; 0135 QWaitCondition condVar; 0136 QList<TodoPair> todo; 0137 }; 0138 0139 // -------------------------------------------------------------------------------------------------- 0140 0141 class Q_DECL_HIDDEN WorkerObjectRunnable : public QRunnable 0142 { 0143 public: 0144 0145 WorkerObjectRunnable(WorkerObject* const object, ParkingThread* const parkingThread); 0146 0147 protected: 0148 0149 WorkerObject* object; 0150 ParkingThread* parkingThread; 0151 0152 protected: 0153 0154 void run() override; 0155 0156 private: 0157 0158 Q_DISABLE_COPY(WorkerObjectRunnable) 0159 }; 0160 0161 // -------------------------------------------------------------------------------------------------- 0162 0163 WorkerObjectRunnable::WorkerObjectRunnable(WorkerObject* const object, ParkingThread* const parkingThread) 0164 : object (object), 0165 parkingThread(parkingThread) 0166 { 0167 setAutoDelete(true); 0168 } 0169 0170 void WorkerObjectRunnable::run() 0171 { 0172 if (!object) 0173 { 0174 return; 0175 } 0176 0177 // if another thread should still be running, wait until the object is parked in ParkingThread 0178 0179 parkingThread->moveToCurrentThread(object); 0180 0181 // The object is in state Scheduled or Deactivating now. 0182 // It won't be deleted until Inactive, and as long a runnable is set. 0183 0184 object->addRunnable(this); 0185 0186 Q_EMIT object->started(); 0187 0188 if (object->transitionToRunning()) 0189 { 0190 QThread::Priority previousPriority = QThread::currentThread()->priority(); 0191 0192 if (object->priority() != QThread::InheritPriority) 0193 { 0194 QThread::currentThread()->setPriority(object->priority()); 0195 } 0196 0197 QEventLoop loop; 0198 object->setEventLoop(&loop); 0199 loop.exec(); 0200 object->setEventLoop(nullptr); 0201 0202 if (previousPriority != QThread::InheritPriority) 0203 { 0204 QThread::currentThread()->setPriority(previousPriority); 0205 } 0206 } 0207 0208 object->transitionToInactive(); 0209 0210 Q_EMIT object->finished(); 0211 0212 // if this is rescheduled, it will wait in the other thread at moveToCurrentThread() above until we park 0213 0214 parkingThread->parkObject(object); 0215 0216 // now, free the object - in case it wants to get deleted 0217 0218 object->removeRunnable(this); 0219 } 0220 0221 // ------------------------------------------------------------------------------------------------- 0222 0223 class Q_DECL_HIDDEN ThreadManager::Private 0224 { 0225 public: 0226 0227 Private() 0228 : parkingThread(nullptr), 0229 pool (nullptr) 0230 { 0231 } 0232 0233 ParkingThread* parkingThread; 0234 QThreadPool* pool; 0235 0236 public: 0237 0238 void changeMaxThreadCount(int diff) 0239 { 0240 pool->setMaxThreadCount(pool->maxThreadCount() + diff); 0241 } 0242 }; 0243 0244 // ------------------------------------------------------------------------------------------------- 0245 0246 class Q_DECL_HIDDEN ThreadManagerCreator 0247 { 0248 public: 0249 0250 ThreadManager object; 0251 }; 0252 0253 Q_GLOBAL_STATIC(ThreadManagerCreator, creator) 0254 0255 // ------------------------------------------------------------------------------------------------- 0256 0257 ThreadManager* ThreadManager::instance() 0258 { 0259 return (&creator->object); 0260 } 0261 0262 ThreadManager::ThreadManager() 0263 : d(new Private) 0264 { 0265 d->parkingThread = new ParkingThread(this); 0266 d->pool = new QThreadPool(this); 0267 0268 d->pool->setMaxThreadCount(QThread::idealThreadCount() + 1); 0269 } 0270 0271 ThreadManager::~ThreadManager() 0272 { 0273 d->pool->waitForDone(); 0274 0275 delete d; 0276 } 0277 0278 void ThreadManager::initialize(WorkerObject* const object) 0279 { 0280 connect(object, SIGNAL(destroyed(QObject*)), 0281 this, SLOT(slotDestroyed(QObject*))); 0282 0283 d->changeMaxThreadCount(+1); 0284 0285 d->parkingThread->parkObject(object); 0286 } 0287 0288 void ThreadManager::initialize(DynamicThread* const dynamicThread) 0289 { 0290 connect(dynamicThread, SIGNAL(destroyed(QObject*)), 0291 this, SLOT(slotDestroyed(QObject*))); 0292 0293 d->changeMaxThreadCount(+1); 0294 } 0295 0296 void ThreadManager::schedule(WorkerObject* object) 0297 { 0298 d->pool->start(new WorkerObjectRunnable(object, d->parkingThread)); 0299 } 0300 0301 void ThreadManager::schedule(QRunnable* runnable) 0302 { 0303 d->pool->start(runnable); 0304 } 0305 0306 void ThreadManager::slotDestroyed(QObject*) 0307 { 0308 d->changeMaxThreadCount(-1); 0309 } 0310 0311 } // namespace Digikam 0312 0313 #include "threadmanager.moc" 0314 0315 #include "moc_threadmanager.cpp"