File indexing completed on 2024-05-12 15:58:48

0001 /*
0002  *  SPDX-FileCopyrightText: 2010 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #ifndef __KIS_UPDATER_CONTEXT_H
0008 #define __KIS_UPDATER_CONTEXT_H
0009 
0010 #include <QMutex>
0011 #include <QReadWriteLock>
0012 #include <QThreadPool>
0013 #include <QWaitCondition>
0014 
0015 #include "kis_base_rects_walker.h"
0016 #include "kis_async_merger.h"
0017 #include "kis_lock_free_lod_counter.h"
0018 
0019 #include "KisUpdaterContextSnapshotEx.h"
0020 #include "kis_update_scheduler.h"
0021 
0022 class KisUpdateJobItem;
0023 class KisSpontaneousJob;
0024 class KisStrokeJob;
0025 class KisUpdateScheduler;
0026 
0027 class KRITAIMAGE_EXPORT KisUpdaterContext
0028 {
0029 public:
0030     static const int useIdealThreadCountTag;
0031 
0032 public:
0033     KisUpdaterContext(qint32 threadCount = useIdealThreadCountTag, KisUpdateScheduler *parent = 0);
0034     ~KisUpdaterContext();
0035 
0036 
0037     /**
0038      * Returns the number of currently running jobs of each type.
0039      * To use this information you should lock the context beforehand.
0040      *
0041      * \see lock()
0042      */
0043     void getJobsSnapshot(qint32 &numMergeJobs, qint32 &numStrokeJobs);
0044 
0045     KisUpdaterContextSnapshotEx getContextSnapshotEx() const;
0046 
0047     /**
0048      * Returns the current level of detail of all the running jobs in the
0049      * context. If there are no jobs, returns -1.
0050      */
0051     int currentLevelOfDetail() const;
0052 
0053     /**
0054      * Check whether there is a spare thread for running
0055      * one more job
0056      */
0057     bool hasSpareThread();
0058 
0059     /**
0060      * Checks whether the walker intersects with any
0061      * of currently executing walkers. If it does,
0062      * it is not allowed to go in. It should be called
0063      * with the lock held.
0064      *
0065      * \see lock()
0066      */
0067     bool isJobAllowed(KisBaseRectsWalkerSP walker);
0068 
0069     /**
0070      * Registers the job and starts executing it.
0071      * The caller must ensure that the context is locked
0072      * with lock(), job is allowed with isWalkerAllowed() and
0073      * there is a spare thread for running it with hasSpareThread()
0074      *
0075      * \see lock()
0076      * \see isWalkerAllowed()
0077      * \see hasSpareThread()
0078      */
0079     void addMergeJob(KisBaseRectsWalkerSP walker);
0080 
0081     /**
0082      * Adds a stroke job to the context. The prerequisites are
0083      * the same as for addMergeJob()
0084      * \see addMergeJob()
0085      */
0086     void addStrokeJob(KisStrokeJob *strokeJob);
0087 
0088 
0089     /**
0090      * Adds a spontaneous job to the context. The prerequisites are
0091      * the same as for addMergeJob()
0092      * \see addMergeJob()
0093      */
0094     void addSpontaneousJob(KisSpontaneousJob *spontaneousJob);
0095 
0096     /**
0097      * Block execution of the caller until all the jobs are finished
0098      */
0099     void waitForDone();
0100 
0101     /**
0102      * Locks the context to guarantee an exclusive access
0103      * to the context
0104      */
0105     void lock();
0106 
0107     /**
0108      * Unlocks the context
0109      *
0110      * \see lock()
0111      */
0112     void unlock();
0113 
0114     /**
0115      * Set the number of threads available for this updater context
0116      * WARNING: one cannot change the number of threads if there is
0117      *          at least one job running in the context! So before
0118      *          calling this method make sure you do two things:
0119      *          1) barrierLock() the update scheduler
0120      *          2) lock() the context
0121      */
0122     void setThreadsLimit(int value);
0123 
0124     /**
0125      * Return the number of available threads in the context. Make sure you
0126      * lock the context before calling this function!
0127      */
0128     int threadsLimit() const;
0129 
0130     void continueUpdate(const QRect& rc);
0131     void doSomeUsefulWork();
0132     void jobFinished();
0133     void jobThreadExited();
0134 
0135     void setTestingMode(bool value);
0136 
0137 protected:
0138     static bool walkerIntersectsJob(KisBaseRectsWalkerSP walker,
0139                                     const KisUpdateJobItem* job);
0140     qint32 findSpareThread();
0141 
0142 protected:
0143     /**
0144      * The lock is shared by all the child update job items.
0145      * When an item wants to run a usual (non-exclusive) job,
0146      * it locks the lock for read access. When an exclusive
0147      * access is requested, it locks it for write
0148      */
0149     QReadWriteLock m_exclusiveJobLock;
0150 
0151     QMutex m_lock;
0152     QMutex m_runningThreadsMutex;
0153     int m_numRunningThreads = 0;
0154     QWaitCondition m_waitForDoneCondition;
0155     QVector<KisUpdateJobItem*> m_jobs;
0156     QThreadPool m_threadPool;
0157     KisLockFreeLodCounter m_lodCounter;
0158     KisUpdateScheduler *m_scheduler;
0159     bool m_testingMode = false;
0160 
0161 private:
0162 
0163     friend class KisUpdaterContextTest;
0164     friend class KisUpdateSchedulerTest;
0165     friend class KisStrokesQueueTest;
0166     friend class KisSimpleUpdateQueueTest;
0167     friend class KisUpdateJobItem;
0168 
0169     const QVector<KisUpdateJobItem*> getJobs();
0170     void clear();
0171 
0172     void startThread(int index);
0173 
0174 };
0175 
0176 class KRITAIMAGE_EXPORT KisTestableUpdaterContext : public KisUpdaterContext
0177 {
0178 public:
0179     /**
0180      * Creates an explicit number of threads
0181      */
0182     KisTestableUpdaterContext(qint32 threadCount);
0183 };
0184 
0185 
0186 #endif /* __KIS_UPDATER_CONTEXT_H */
0187 
0188 
0189