1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
/* ============================================================
 *
 * This file is a part of digiKam project
 * https://www.digikam.org
 *
 * Date        : 2012-01-13
 * Description : Multithreaded worker objects, working in parallel
 *
 * SPDX-FileCopyrightText: 2010-2012 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * ============================================================ */

#pragma once

// Qt includes

#include <QObject>

// Local includes

#include "digikam_export.h"
#include "workerobject.h"

namespace Digikam
{

class DIGIKAM_EXPORT ParallelWorkers
{

public:

    /**
     * ParallelWorkers is a helper class to distribute work over
     * several identical workers objects.
     * See ParallelAdapter for guidance how to use it.
     */
    ParallelWorkers() = default;
    virtual ~ParallelWorkers();

    /**
     * The corresponding methods of all added worker objects will be called
     */
    void schedule();<--- Parent function 'ParallelWorkers::schedule'<--- Parent function 'ParallelWorkers::schedule'
    void deactivate(WorkerObject::DeactivatingMode mode = WorkerObject::FlushSignals);<--- Parent function 'ParallelWorkers::deactivate'<--- Parent function 'ParallelWorkers::deactivate'
    void wait();<--- Parent function 'ParallelWorkers::wait'<--- Parent function 'ParallelWorkers::wait'

    void setPriority(QThread::Priority priority);

    /**
     * Returns true if the current number of added workers has reached the optimalWorkerCount()
     */
    bool optimalWorkerCountReached()                            const;

    /**
     * Regarding the number of logical CPUs on the current machine,
     * returns the optimal count of concurrent workers
     */
    static int optimalWorkerCount();

public:

    /// Connects signals outbound from all workers to a given receiver

    bool connect(const char* signal,<--- Parent function 'ParallelWorkers::connect'<--- Parent function 'ParallelWorkers::connect'
                 const QObject* receiver,
                 const char* method,
                 Qt::ConnectionType type = Qt::AutoConnection)  const;

protected:

    void add(WorkerObject* const worker);

    // Internal implementation

    /**
     * Replaces slot call distribution of the target QObject
     */
    int replacementQtMetacall(QMetaObject::Call _c, int _id, void** _a);
    const QMetaObject* replacementMetaObject()                  const;

    /**
     * Return the target QObject (double inheritance)
     */
    virtual QObject* asQObject()                                                        = 0;

    /**
     * The qt_metacall of WorkerObject, one level above the target QObject
     */
    virtual int WorkerObjectQtMetacall(QMetaObject::Call _c, int _id, void** _a)        = 0;

    /**
     * The moc-generated metaObject of the target object
     */
    virtual const QMetaObject* mocMetaObject()                  const                   = 0;

    int replacementStaticQtMetacall(QMetaObject::Call _c, int _id, void** _a);
    typedef void (*StaticMetacallFunction)(QObject*, QMetaObject::Call, int, void**);
    virtual StaticMetacallFunction staticMetacallPointer()                              = 0;

protected:

    QList<WorkerObject*>   m_workers;
    int                    m_currentIndex           = 0;
    QMetaObject*           m_replacementMetaObject  = nullptr;

    StaticMetacallFunction m_originalStaticMetacall = nullptr;

private:

    // Disable
    ParallelWorkers(const ParallelWorkers&)            = delete;
    ParallelWorkers& operator=(const ParallelWorkers&) = delete;
};

// -------------------------------------------------------------------------------------------------

template <class A>

class ParallelAdapter : public A,
                        public ParallelWorkers
{
public:

    /**
     * Instead of using a single WorkerObject, create a ParallelAdapter for
     * your worker object subclass, and add() individual WorkerObjects.
     * The load will be evenly distributed.
     * Note: unlike with WorkerObject directly, there is no need to call schedule().
     * For inbound connections (signals connected to a WorkerObject's slot, to be processed,
     * use a Qt::DirectConnection on the adapter.
     * For outbound connections (signals emitted from the WorkerObject),
     * use ParallelAdapter's connect to have a connection from all added WorkerObjects.
     */
    ParallelAdapter()                                                             = default;
    ~ParallelAdapter()                                                   override = default;

    void add(A* const worker)
    {
        ParallelWorkers::add(worker);
    }

    // Internal Implementation
    // I know this is a hack

    int WorkerObjectQtMetacall(QMetaObject::Call _c, int _id, void** _a) override
    {
        return WorkerObject::qt_metacall(_c, _id, _a);
    }

    const QMetaObject* mocMetaObject()                             const override
    {
        return A::metaObject();
    }

    static void qt_static_metacall(QObject* o, QMetaObject::Call _c, int _id, void** _a)
    {
        static_cast<ParallelAdapter*>(o)->replacementStaticQtMetacall(_c, _id, _a);
    }

    StaticMetacallFunction staticMetacallPointer()                      override
    {
        return qt_static_metacall;
    }

    const QMetaObject* metaObject()                                const override
    {
        return ParallelWorkers::replacementMetaObject();
    }

    int qt_metacall(QMetaObject::Call _c, int _id, void** _a)            override
    {
        return ParallelWorkers::replacementQtMetacall(_c, _id, _a);
    }

    QObject* asQObject()                                                 override
    {
        return this;
    }

    void schedule()<--- Derived function 'ParallelAdapter::schedule'<--- Derived function 'ParallelAdapter < FileWorkerInterface >::schedule'<--- Derived function 'ParallelAdapter < FileWorkerInterface >::schedule'<--- Derived function 'ParallelAdapter::schedule'
    {
        ParallelWorkers::schedule();
    }

    void deactivate(WorkerObject::DeactivatingMode mode = WorkerObject::FlushSignals)<--- Derived function 'ParallelAdapter::deactivate'<--- Derived function 'ParallelAdapter < FileWorkerInterface >::deactivate'<--- Derived function 'ParallelAdapter::deactivate'
    {
        ParallelWorkers::deactivate(mode);
    }

    void wait()<--- Derived function 'ParallelAdapter::wait'<--- Derived function 'ParallelAdapter < FileWorkerInterface >::wait'<--- Derived function 'ParallelAdapter < FileWorkerInterface >::wait'<--- Derived function 'ParallelAdapter::wait'
    {
        ParallelWorkers::wait();
    }

    bool connect(const char* signal,<--- Derived function 'ParallelAdapter::connect'<--- Derived function 'ParallelAdapter < FileWorkerInterface >::connect'<--- Derived function 'ParallelAdapter::connect'
                 const QObject* receiver,
                 const char* method,
                 Qt::ConnectionType type = Qt::AutoConnection)     const
    {
        return ParallelWorkers::connect(signal, receiver, method, type);
    }

private:

    // Disable
    ParallelAdapter(const ParallelAdapter&)            = delete;
    ParallelAdapter& operator=(const ParallelAdapter&) = delete;
};

} // namespace Digikam