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