File indexing completed on 2025-03-09 03:56:11

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2012-01-13
0007  * Description : Multithreaded worker objects, working in parallel
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 #ifndef DIGIKAM_PARALLEL_WORKERS_H
0016 #define DIGIKAM_PARALLEL_WORKERS_H
0017 
0018 // Qt includes
0019 
0020 #include <QObject>
0021 
0022 // Local includes
0023 
0024 #include "digikam_export.h"
0025 #include "workerobject.h"
0026 
0027 namespace Digikam
0028 {
0029 
0030 class DIGIKAM_EXPORT ParallelWorkers
0031 {
0032 
0033 public:
0034 
0035     /**
0036      * ParallelWorkers is a helper class to distribute work over
0037      * several identical workers objects.
0038      * See ParallelAdapter for guidance how to use it.
0039      */
0040     explicit ParallelWorkers();
0041     virtual ~ParallelWorkers();
0042 
0043     /**
0044      * The corresponding methods of all added worker objects will be called
0045      */
0046     void schedule();
0047     void deactivate(WorkerObject::DeactivatingMode mode = WorkerObject::FlushSignals);
0048     void wait();
0049 
0050     void setPriority(QThread::Priority priority);
0051 
0052     /**
0053      * Returns true if the current number of added workers has reached the optimalWorkerCount()
0054      */
0055     bool optimalWorkerCountReached()                            const;
0056 
0057     /**
0058      * Regarding the number of logical CPUs on the current machine,
0059      * returns the optimal count of concurrent workers
0060      */
0061     static int optimalWorkerCount();
0062 
0063 public:
0064 
0065     /// Connects signals outbound from all workers to a given receiver
0066 
0067     bool connect(const char* signal,
0068                  const QObject* receiver,
0069                  const char* method,
0070                  Qt::ConnectionType type = Qt::AutoConnection)  const;
0071 
0072 protected:
0073 
0074     void add(WorkerObject* const worker);
0075 
0076     // Internal implementation
0077 
0078     /**
0079      * Replaces slot call distribution of the target QObject
0080      */
0081     int replacementQtMetacall(QMetaObject::Call _c, int _id, void** _a);
0082     const QMetaObject* replacementMetaObject()                  const;
0083 
0084     /**
0085      * Return the target QObject (double inheritance)
0086      */
0087     virtual QObject* asQObject()                                                        = 0;
0088 
0089     /**
0090      * The qt_metacall of WorkerObject, one level above the target QObject
0091      */
0092     virtual int WorkerObjectQtMetacall(QMetaObject::Call _c, int _id, void** _a)        = 0;
0093 
0094     /**
0095      * The moc-generated metaObject of the target object
0096      */
0097     virtual const QMetaObject* mocMetaObject()                  const                   = 0;
0098 
0099     int replacementStaticQtMetacall(QMetaObject::Call _c, int _id, void** _a);
0100     typedef void (*StaticMetacallFunction)(QObject*, QMetaObject::Call, int, void**);
0101     virtual StaticMetacallFunction staticMetacallPointer()                              = 0;
0102 
0103 protected:
0104 
0105     QList<WorkerObject*>   m_workers;
0106     int                    m_currentIndex;
0107     QMetaObject*           m_replacementMetaObject;
0108 
0109     StaticMetacallFunction m_originalStaticMetacall;
0110 
0111 private:
0112 
0113     // Disable
0114     ParallelWorkers(const ParallelWorkers&)            = delete;
0115     ParallelWorkers& operator=(const ParallelWorkers&) = delete;
0116 };
0117 
0118 // -------------------------------------------------------------------------------------------------
0119 
0120 template <class A>
0121 
0122 class ParallelAdapter : public A, public ParallelWorkers
0123 {
0124 public:
0125 
0126     /**
0127      * Instead of using a single WorkerObject, create a ParallelAdapter for
0128      * your worker object subclass, and add() individual WorkerObjects.
0129      * The load will be evenly distributed.
0130      * Note: unlike with WorkerObject directly, there is no need to call schedule().
0131      * For inbound connections (signals connected to a WorkerObject's slot, to be processed,
0132      * use a Qt::DirectConnection on the adapter.
0133      * For outbound connections (signals emitted from the WorkerObject),
0134      * use ParallelAdapter's connect to have a connection from all added WorkerObjects.
0135      */
0136     explicit ParallelAdapter()
0137     {
0138     }
0139 
0140     ~ParallelAdapter()                                                   override
0141     {
0142     }
0143 
0144     void add(A* const worker)
0145     {
0146         ParallelWorkers::add(worker);
0147     }
0148 
0149     // Internal Implementation
0150     // I know this is a hack
0151 
0152     int WorkerObjectQtMetacall(QMetaObject::Call _c, int _id, void** _a) override
0153     {
0154         return WorkerObject::qt_metacall(_c, _id, _a);
0155     }
0156 
0157     const QMetaObject* mocMetaObject()                             const override
0158     {
0159         return A::metaObject();
0160     }
0161 
0162     static void qt_static_metacall(QObject* o, QMetaObject::Call _c, int _id, void** _a)
0163     {
0164         static_cast<ParallelAdapter*>(o)->replacementStaticQtMetacall(_c, _id, _a);
0165     }
0166 
0167     StaticMetacallFunction staticMetacallPointer()                      override
0168     {
0169         return qt_static_metacall;
0170     }
0171 
0172     const QMetaObject* metaObject()                                const override
0173     {
0174         return ParallelWorkers::replacementMetaObject();
0175     }
0176 
0177     int qt_metacall(QMetaObject::Call _c, int _id, void** _a)            override
0178     {
0179         return ParallelWorkers::replacementQtMetacall(_c, _id, _a);
0180     }
0181 
0182     QObject* asQObject()                                                 override
0183     {
0184         return this;
0185     }
0186 
0187     void schedule()
0188     {
0189         ParallelWorkers::schedule();
0190     }
0191 
0192     void deactivate(WorkerObject::DeactivatingMode mode = WorkerObject::FlushSignals)
0193     {
0194         ParallelWorkers::deactivate(mode);
0195     }
0196 
0197     void wait()
0198     {
0199         ParallelWorkers::wait();
0200     }
0201 
0202     bool connect(const char* signal,
0203                  const QObject* receiver,
0204                  const char* method,
0205                  Qt::ConnectionType type = Qt::AutoConnection)     const
0206     {
0207         return ParallelWorkers::connect(signal, receiver, method, type);
0208     }
0209 
0210 private:
0211 
0212     // Disable
0213     ParallelAdapter(const ParallelAdapter&)            = delete;
0214     ParallelAdapter& operator=(const ParallelAdapter&) = delete;
0215 };
0216 
0217 } // namespace Digikam
0218 
0219 #endif // DIGIKAM_PARALLEL_WORKERS_H