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-06 0007 * Description : Multithreaded worker object 0008 * 0009 * SPDX-FileCopyrightText: 2010-2012 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0010 * SPDX-FileCopyrightText: 2012-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #include "workerobject.h" 0017 0018 // Qt includes 0019 0020 #include <QCoreApplication> 0021 #include <QEvent> 0022 #include <QMutex> 0023 #include <QWaitCondition> 0024 0025 // Local includes 0026 0027 #include "digikam_debug.h" 0028 #include "threadmanager.h" 0029 0030 namespace Digikam 0031 { 0032 0033 class Q_DECL_HIDDEN WorkerObject::Private 0034 { 0035 public: 0036 0037 Private() 0038 : state (WorkerObject::Inactive), 0039 eventLoop (nullptr), 0040 runnable (nullptr), 0041 inDestruction (false), 0042 priority (QThread::InheritPriority) 0043 { 0044 } 0045 0046 volatile WorkerObject::State state; 0047 QMutex mutex; 0048 QWaitCondition condVar; 0049 QEventLoop* eventLoop; 0050 WorkerObjectRunnable* runnable; 0051 bool inDestruction; 0052 QThread::Priority priority; 0053 }; 0054 0055 WorkerObject::WorkerObject() 0056 : d(new Private) 0057 { 0058 ThreadManager::instance()->initialize(this); 0059 } 0060 0061 WorkerObject::~WorkerObject() 0062 { 0063 shutDown(); 0064 0065 delete d; 0066 } 0067 0068 void WorkerObject::shutDown() 0069 { 0070 { 0071 QMutexLocker locker(&d->mutex); 0072 d->inDestruction = true; 0073 } 0074 0075 deactivate(PhaseOut); 0076 wait(); 0077 } 0078 0079 void WorkerObject::wait() 0080 { 0081 QMutexLocker locker(&d->mutex); 0082 0083 while ((d->state != Inactive) || d->runnable) 0084 { 0085 d->condVar.wait(&d->mutex); 0086 } 0087 } 0088 0089 bool WorkerObject::connectAndSchedule(const QObject* sender, 0090 const char* signal, 0091 const char* method, 0092 Qt::ConnectionType type) const 0093 { 0094 connect(sender, signal, 0095 this, SLOT(schedule()), 0096 Qt::DirectConnection); 0097 0098 return (QObject::connect(sender, signal, 0099 method, 0100 type)); 0101 } 0102 0103 bool WorkerObject::connectAndSchedule(const QObject* sender, 0104 const char* signal, 0105 const WorkerObject* receiver, 0106 const char* method, 0107 Qt::ConnectionType type) 0108 { 0109 connect(sender, signal, 0110 receiver, SLOT(schedule()), 0111 Qt::DirectConnection); 0112 0113 return (QObject::connect(sender, signal, 0114 receiver, method, 0115 type)); 0116 } 0117 0118 bool WorkerObject::disconnectAndSchedule(const QObject* sender, 0119 const char* signal, 0120 const WorkerObject* receiver, 0121 const char* method) 0122 { 0123 disconnect(sender, signal, 0124 receiver, SLOT(schedule())); 0125 0126 return (QObject::disconnect(sender, signal, 0127 receiver, method)); 0128 } 0129 0130 WorkerObject::State WorkerObject::state() const 0131 { 0132 return (d->state); 0133 } 0134 0135 void WorkerObject::setPriority(QThread::Priority priority) 0136 { 0137 if (d->priority == priority) 0138 { 0139 return; 0140 } 0141 0142 d->priority = priority; 0143 0144 if (d->priority != QThread::InheritPriority) 0145 { 0146 QMutexLocker locker(&d->mutex); 0147 0148 if (d->state == Running) 0149 { 0150 thread()->setPriority(d->priority); 0151 } 0152 } 0153 } 0154 0155 QThread::Priority WorkerObject::priority() const 0156 { 0157 return (d->priority); 0158 } 0159 0160 bool WorkerObject::event(QEvent* e) 0161 { 0162 if (e->type() == QEvent::User) 0163 { 0164 aboutToQuitLoop(); 0165 d->eventLoop->quit(); 0166 0167 return true; 0168 } 0169 0170 return (QObject::event(e)); 0171 } 0172 0173 void WorkerObject::aboutToQuitLoop() 0174 { 0175 } 0176 0177 void WorkerObject::setEventLoop(QEventLoop* loop) 0178 { 0179 d->eventLoop = loop; 0180 } 0181 0182 void WorkerObject::addRunnable(WorkerObjectRunnable* runnable) 0183 { 0184 QMutexLocker locker(&d->mutex); 0185 d->runnable = runnable; 0186 } 0187 0188 void WorkerObject::removeRunnable(WorkerObjectRunnable* runnable) 0189 { 0190 QMutexLocker locker(&d->mutex); 0191 0192 // There could be a second runnable in the meantime, waiting for the other, leaving runnable to park 0193 0194 if (d->runnable == runnable) 0195 { 0196 d->runnable = nullptr; 0197 } 0198 0199 d->condVar.wakeAll(); 0200 } 0201 0202 void WorkerObject::schedule() 0203 { 0204 { 0205 QMutexLocker locker(&d->mutex); 0206 0207 if (d->inDestruction) 0208 { 0209 return; 0210 } 0211 0212 switch (d->state) 0213 { 0214 case Inactive: 0215 case Deactivating: 0216 { 0217 d->state = Scheduled; 0218 break; 0219 } 0220 0221 case Scheduled: 0222 case Running: 0223 { 0224 return; 0225 } 0226 } 0227 } 0228 0229 ThreadManager::instance()->schedule(this); 0230 } 0231 0232 void WorkerObject::deactivate(DeactivatingMode mode) 0233 { 0234 { 0235 QMutexLocker locker(&d->mutex); 0236 0237 switch (d->state) 0238 { 0239 case Scheduled: 0240 case Running: 0241 { 0242 d->state = Deactivating; 0243 break; 0244 } 0245 0246 case Inactive: 0247 case Deactivating: 0248 { 0249 return; 0250 } 0251 } 0252 } 0253 0254 // NOTE: use dynamic binding as this virtual method can be re-implemented in derived classes. 0255 0256 this->aboutToDeactivate(); 0257 0258 if (mode == FlushSignals) 0259 { 0260 QCoreApplication::removePostedEvents(this, QEvent::MetaCall); 0261 } 0262 0263 // Cannot say that this is thread-safe: thread()->quit(); 0264 0265 if (mode == KeepSignals) 0266 { 0267 QCoreApplication::postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority); 0268 } 0269 else 0270 { 0271 QCoreApplication::postEvent(this, new QEvent(QEvent::User)); 0272 } 0273 } 0274 0275 void WorkerObject::aboutToDeactivate() 0276 { 0277 } 0278 0279 bool WorkerObject::transitionToRunning() 0280 { 0281 QMutexLocker locker(&d->mutex); 0282 0283 switch (d->state) 0284 { 0285 case Running: 0286 case Scheduled: 0287 { 0288 d->state = Running; 0289 return true; 0290 } 0291 0292 case Deactivating: 0293 default: 0294 { 0295 return false; 0296 } 0297 } 0298 } 0299 0300 void WorkerObject::transitionToInactive() 0301 { 0302 QMutexLocker locker(&d->mutex); 0303 0304 switch (d->state) 0305 { 0306 case Scheduled: 0307 { 0308 break; 0309 } 0310 0311 case Deactivating: 0312 default: 0313 { 0314 d->state = Inactive; 0315 d->condVar.wakeAll(); 0316 break; 0317 } 0318 } 0319 } 0320 0321 } // namespace Digikam 0322 0323 #include "moc_workerobject.cpp"