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"