Krita Source Code Documentation
Loading...
Searching...
No Matches
KisUpdaterContext Class Reference

#include <kis_updater_context.h>

+ Inheritance diagram for KisUpdaterContext:

Public Member Functions

void addMergeJob (KisBaseRectsWalkerSP walker)
 
void addSpontaneousJob (KisSpontaneousJob *spontaneousJob)
 
void addStrokeJob (KisStrokeJob *strokeJob)
 
void continueUpdate (const QRect &rc)
 
int currentLevelOfDetail () const
 
void doSomeUsefulWork ()
 
KisUpdaterContextSnapshotEx getContextSnapshotEx () const
 
void getJobsSnapshot (qint32 &numMergeJobs, qint32 &numStrokeJobs)
 
bool hasSpareThread ()
 
bool isIdle () const
 
bool isJobAllowed (KisBaseRectsWalkerSP walker)
 
void jobFinished ()
 
void jobThreadExited ()
 
 KisUpdaterContext (qint32 threadCount=useIdealThreadCountTag, KisUpdateScheduler *parent=0)
 
void lock ()
 
void setTestingMode (bool value)
 
void setThreadsLimit (int value)
 
int threadsLimit () const
 
void unlock ()
 
void waitForDone ()
 
 ~KisUpdaterContext ()
 

Static Public Attributes

static const int useIdealThreadCountTag = -1
 

Protected Member Functions

qint32 findSpareThread ()
 

Static Protected Member Functions

static bool walkerIntersectsJob (KisBaseRectsWalkerSP walker, const KisUpdateJobItem *job)
 

Protected Attributes

QReadWriteLock m_exclusiveJobLock
 
QVector< KisUpdateJobItem * > m_jobs
 
QMutex m_lock
 
KisLockFreeLodCounter m_lodCounter
 
int m_numRunningThreads = 0
 
QMutex m_runningThreadsMutex
 
KisUpdateSchedulerm_scheduler
 
bool m_testingMode = false
 
QThreadPool m_threadPool
 
QWaitCondition m_waitForDoneCondition
 

Private Member Functions

void clear ()
 
const QVector< KisUpdateJobItem * > getJobs ()
 
void startThread (int index)
 

Friends

class KisSimpleUpdateQueueTest
 
class KisStrokesQueueTest
 
class KisUpdateJobItem
 
class KisUpdaterContextTest
 
class KisUpdateSchedulerTest
 

Detailed Description

Definition at line 27 of file kis_updater_context.h.

Constructor & Destructor Documentation

◆ KisUpdaterContext()

KisUpdaterContext::KisUpdaterContext ( qint32 threadCount = useIdealThreadCountTag,
KisUpdateScheduler * parent = 0 )

Definition at line 18 of file kis_updater_context.cpp.

19 : m_scheduler(parent)
20{
21 if(threadCount <= 0) {
22 threadCount = QThread::idealThreadCount();
23 threadCount = threadCount > 0 ? threadCount : 1;
24 }
25
26 setThreadsLimit(threadCount);
27}
void setThreadsLimit(int value)
KisUpdateScheduler * m_scheduler

References setThreadsLimit().

◆ ~KisUpdaterContext()

KisUpdaterContext::~KisUpdaterContext ( )

Definition at line 29 of file kis_updater_context.cpp.

30{
31 m_threadPool.waitForDone();
32
33 if (m_testingMode) {
34 clear();
35 }
36
37 qDeleteAll(m_jobs);
38}
QVector< KisUpdateJobItem * > m_jobs

References clear(), m_jobs, m_testingMode, and m_threadPool.

Member Function Documentation

◆ addMergeJob()

void KisUpdaterContext::addMergeJob ( KisBaseRectsWalkerSP walker)

Registers the job and starts executing it. The caller must ensure that the context is locked with lock(), job is allowed with isWalkerAllowed() and there is a spare thread for running it with hasSpareThread()

See also
lock()
isWalkerAllowed()
hasSpareThread()

NOTE: In theory, isJobAllowed() and addMergeJob() should be merged into one atomic method like bool push(), because this implementation of KisUpdaterContext will not work in case of multiple producers. But currently we have only one producer (one thread in a time), that is guaranteed by the lock()/unlock() pair in KisAbstractUpdateQueue::processQueue.

Definition at line 164 of file kis_updater_context.cpp.

165{
167 qint32 jobIndex = findSpareThread();
168 Q_ASSERT(jobIndex >= 0);
169
170 const bool shouldStartThread = m_jobs[jobIndex]->setWalker(walker);
171
172 // it might happen that we call this function from within
173 // the thread itself, right when it finished its work
174 if (shouldStartThread && !m_testingMode) {
175 startThread(jobIndex);
176 }
177}
KisLockFreeLodCounter m_lodCounter
void startThread(int index)

References KisLockFreeLodCounter::addLod(), findSpareThread(), KisBaseRectsWalker::levelOfDetail(), m_jobs, m_lodCounter, m_testingMode, and startThread().

◆ addSpontaneousJob()

void KisUpdaterContext::addSpontaneousJob ( KisSpontaneousJob * spontaneousJob)

Adds a spontaneous job to the context. The prerequisites are the same as for addMergeJob()

See also
addMergeJob()

Definition at line 194 of file kis_updater_context.cpp.

195{
196 m_lodCounter.addLod(spontaneousJob->levelOfDetail());
197 qint32 jobIndex = findSpareThread();
198 Q_ASSERT(jobIndex >= 0);
199
200 const bool shouldStartThread = m_jobs[jobIndex]->setSpontaneousJob(spontaneousJob);
201
202 // it might happen that we call this function from within
203 // the thread itself, right when it finished its work
204 if (shouldStartThread && !m_testingMode) {
205 startThread(jobIndex);
206 }
207}
virtual int levelOfDetail() const =0

References KisLockFreeLodCounter::addLod(), findSpareThread(), KisSpontaneousJob::levelOfDetail(), m_jobs, m_lodCounter, m_testingMode, and startThread().

◆ addStrokeJob()

void KisUpdaterContext::addStrokeJob ( KisStrokeJob * strokeJob)

Adds a stroke job to the context. The prerequisites are the same as for addMergeJob()

See also
addMergeJob()

Definition at line 179 of file kis_updater_context.cpp.

180{
181 m_lodCounter.addLod(strokeJob->levelOfDetail());
182 qint32 jobIndex = findSpareThread();
183 Q_ASSERT(jobIndex >= 0);
184
185 const bool shouldStartThread = m_jobs[jobIndex]->setStrokeJob(strokeJob);
186
187 // it might happen that we call this function from within
188 // the thread itself, right when it finished its work
189 if (shouldStartThread && !m_testingMode) {
190 startThread(jobIndex);
191 }
192}
int levelOfDetail() const

References KisLockFreeLodCounter::addLod(), findSpareThread(), KisStrokeJob::levelOfDetail(), m_jobs, m_lodCounter, m_testingMode, and startThread().

◆ clear()

void KisUpdaterContext::clear ( )
private

◆ continueUpdate()

void KisUpdaterContext::continueUpdate ( const QRect & rc)

Definition at line 283 of file kis_updater_context.cpp.

284{
286}
void continueUpdate(const QRect &rect)

References KisUpdateScheduler::continueUpdate(), and m_scheduler.

◆ currentLevelOfDetail()

int KisUpdaterContext::currentLevelOfDetail ( ) const

Returns the current level of detail of all the running jobs in the context. If there are no jobs, returns -1.

Definition at line 99 of file kis_updater_context.cpp.

100{
101 return m_lodCounter.readLod();
102}

References m_lodCounter, and KisLockFreeLodCounter::readLod().

◆ doSomeUsefulWork()

void KisUpdaterContext::doSomeUsefulWork ( )

◆ findSpareThread()

qint32 KisUpdaterContext::findSpareThread ( )
protected

Definition at line 238 of file kis_updater_context.cpp.

239{
240 for(qint32 i=0; i < m_jobs.size(); i++)
241 if(!m_jobs[i]->isRunning())
242 return i;
243
244 return -1;
245}

References m_jobs.

◆ getContextSnapshotEx()

KisUpdaterContextSnapshotEx KisUpdaterContext::getContextSnapshotEx ( ) const

We cannot use Q_FOREACH here since the function may be called concurrently without any locks, causing detaching of the vector and causing a crash. Only read-only accesses are allowed in such environment

Definition at line 64 of file kis_updater_context.cpp.

65{
66 KisUpdaterContextSnapshotEx state = ContextEmpty;
67
74 for (const KisUpdateJobItem *item : std::as_const(m_jobs)) {
75 if (item->type() == KisUpdateJobItem::Type::MERGE ||
77 state |= HasMergeJob;
78 } else if(item->type() == KisUpdateJobItem::Type::STROKE) {
79 switch (item->strokeJobSequentiality()) {
81 state |= HasSequentialJob;
82 break;
84 state |= HasConcurrentJob;
85 break;
87 state |= HasBarrierJob;
88 break;
91 break;
92 }
93 }
94 }
95
96 return state;
97}

References KisStrokeJobData::BARRIER, KisStrokeJobData::CONCURRENT, ContextEmpty, HasBarrierJob, HasConcurrentJob, HasMergeJob, HasSequentialJob, HasUniquelyConcurrentJob, m_jobs, KisUpdateJobItem::MERGE, KisStrokeJobData::SEQUENTIAL, KisUpdateJobItem::SPONTANEOUS, KisUpdateJobItem::STROKE, and KisStrokeJobData::UNIQUELY_CONCURRENT.

◆ getJobs()

const QVector< KisUpdateJobItem * > KisUpdaterContext::getJobs ( )
private

Definition at line 315 of file kis_updater_context.cpp.

316{
317 return m_jobs;
318}

References m_jobs.

◆ getJobsSnapshot()

void KisUpdaterContext::getJobsSnapshot ( qint32 & numMergeJobs,
qint32 & numStrokeJobs )

Returns the number of currently running jobs of each type. To use this information you should lock the context beforehand.

See also
lock()

We cannot use Q_FOREACH here since the function may be called concurrently without any locks, causing detaching of the vector and causing a crash. Only read-only accesses are allowed in such environment

Definition at line 40 of file kis_updater_context.cpp.

42{
43 numMergeJobs = 0;
44 numStrokeJobs = 0;
45
52 for (const KisUpdateJobItem *item : std::as_const(m_jobs)) {
53
54 if(item->type() == KisUpdateJobItem::Type::MERGE ||
56 numMergeJobs++;
57 }
58 else if(item->type() == KisUpdateJobItem::Type::STROKE) {
59 numStrokeJobs++;
60 }
61 }
62}

References m_jobs, KisUpdateJobItem::MERGE, KisUpdateJobItem::SPONTANEOUS, and KisUpdateJobItem::STROKE.

◆ hasSpareThread()

bool KisUpdaterContext::hasSpareThread ( )

Check whether there is a spare thread for running one more job

We cannot use Q_FOREACH here since the function may be called concurrently without any locks, causing detaching of the vector and causing a crash. Only read-only accesses are allowed in such environment

Definition at line 104 of file kis_updater_context.cpp.

105{
106 bool found = false;
107
114 for (const KisUpdateJobItem *item : std::as_const(m_jobs)) {
115 if(!item->isRunning()) {
116 found = true;
117 break;
118 }
119 }
120 return found;
121}

References m_jobs.

◆ isIdle()

bool KisUpdaterContext::isIdle ( ) const

Returns true if there are no jobs running in the context

If there is any contestion on the context's mutex, the function will simply return false, whatever context's state really is. Obviously, if there is a contention, the object is not "idle" :)

Definition at line 218 of file kis_updater_context.cpp.

219{
220 std::unique_lock<QMutex> locker(m_runningThreadsMutex, std::try_to_lock);
221 if (!locker.owns_lock()) return false;
222
223 return m_numRunningThreads == 0;
224}

References m_numRunningThreads, and m_runningThreadsMutex.

◆ isJobAllowed()

bool KisUpdaterContext::isJobAllowed ( KisBaseRectsWalkerSP walker)

Checks whether the walker intersects with any of currently executing walkers. If it does, it is not allowed to go in. It should be called with the lock held.

See also
lock()

We cannot use Q_FOREACH here since the function may be called concurrently without any locks, causing detaching of the vector and causing a crash. Only read-only accesses are allowed in such environment

Definition at line 123 of file kis_updater_context.cpp.

124{
125 int lod = this->currentLevelOfDetail();
126 if (lod >= 0 && walker->levelOfDetail() != lod) return false;
127
128 bool intersects = false;
129
136 for (const KisUpdateJobItem *item : std::as_const(m_jobs)) {
137 if(item->isRunning() && walkerIntersectsJob(walker, item)) {
138 intersects = true;
139 break;
140 }
141 }
142
143 return !intersects;
144}
static bool walkerIntersectsJob(KisBaseRectsWalkerSP walker, const KisUpdateJobItem *job)

References currentLevelOfDetail(), KisBaseRectsWalker::levelOfDetail(), m_jobs, and walkerIntersectsJob().

◆ jobFinished()

◆ jobThreadExited()

void KisUpdaterContext::jobThreadExited ( )

Definition at line 299 of file kis_updater_context.cpp.

300{
301 QMutexLocker l(&m_runningThreadsMutex);
304
305 if (m_numRunningThreads <= 0) {
306 m_waitForDoneCondition.wakeAll();
307 }
308}
QWaitCondition m_waitForDoneCondition
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130

References KIS_SAFE_ASSERT_RECOVER_NOOP, m_numRunningThreads, m_runningThreadsMutex, and m_waitForDoneCondition.

◆ lock()

void KisUpdaterContext::lock ( )

Locks the context to guarantee an exclusive access to the context

Definition at line 247 of file kis_updater_context.cpp.

248{
249 m_lock.lock();
250}

References m_lock.

◆ setTestingMode()

void KisUpdaterContext::setTestingMode ( bool value)

Definition at line 310 of file kis_updater_context.cpp.

311{
313}
float value(const T *src, size_t ch)

References m_testingMode, and value().

◆ setThreadsLimit()

void KisUpdaterContext::setThreadsLimit ( int value)

Set the number of threads available for this updater context WARNING: one cannot change the number of threads if there is at least one job running in the context! So before calling this method make sure you do two things: 1) barrierLock() the update scheduler 2) lock() the context

Definition at line 257 of file kis_updater_context.cpp.

258{
259 m_threadPool.setMaxThreadCount(value);
260
261 for (int i = 0; i < m_jobs.size(); i++) {
262 KIS_SAFE_ASSERT_RECOVER_RETURN(!m_jobs[i]->isRunning());
263 // don't delete the jobs until all of them are checked!
264 }
265
266 for (int i = 0; i < m_jobs.size(); i++) {
267 delete m_jobs[i];
268 }
269
270 m_jobs.resize(value);
271
272 for(qint32 i = 0; i < m_jobs.size(); i++) {
273 m_jobs[i] = new KisUpdateJobItem(this);
274 }
275}
friend class KisUpdateJobItem
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128

References KIS_SAFE_ASSERT_RECOVER_RETURN, KisUpdateJobItem, m_jobs, m_threadPool, and value().

◆ startThread()

void KisUpdaterContext::startThread ( int index)
private

Definition at line 146 of file kis_updater_context.cpp.

147{
148 {
149 QMutexLocker l(&m_runningThreadsMutex);
151 }
152
153 m_threadPool.start(m_jobs[index]);
154}

References m_jobs, m_numRunningThreads, m_runningThreadsMutex, and m_threadPool.

◆ threadsLimit()

int KisUpdaterContext::threadsLimit ( ) const

Return the number of available threads in the context. Make sure you lock the context before calling this function!

Definition at line 277 of file kis_updater_context.cpp.

278{
279 KIS_SAFE_ASSERT_RECOVER_NOOP(m_jobs.size() == m_threadPool.maxThreadCount());
280 return m_jobs.size();
281}

References KIS_SAFE_ASSERT_RECOVER_NOOP, m_jobs, and m_threadPool.

◆ unlock()

void KisUpdaterContext::unlock ( )

Unlocks the context

See also
lock()

Definition at line 252 of file kis_updater_context.cpp.

253{
254 m_lock.unlock();
255}

References m_lock.

◆ waitForDone()

void KisUpdaterContext::waitForDone ( )

Block execution of the caller until all the jobs are finished

Definition at line 209 of file kis_updater_context.cpp.

210{
211 QMutexLocker l(&m_runningThreadsMutex);
212
213 while(m_numRunningThreads > 0) {
214 m_waitForDoneCondition.wait(l.mutex());
215 }
216}

References m_numRunningThreads, m_runningThreadsMutex, and m_waitForDoneCondition.

◆ walkerIntersectsJob()

bool KisUpdaterContext::walkerIntersectsJob ( KisBaseRectsWalkerSP walker,
const KisUpdateJobItem * job )
staticprotected

TODO: theoretically, we should compare rects intersection on a per-layer basis. The current solution is too rough and basically makes updates single-threaded in some cases (e.g. when a transform mask is present in the stack)

Definition at line 226 of file kis_updater_context.cpp.

228{
235 return walker->accessRect().intersects(job->accessRect());
236}
const QRect & accessRect() const

References KisBaseRectsWalker::accessRect(), and KisUpdateJobItem::accessRect().

Friends And Related Symbol Documentation

◆ KisSimpleUpdateQueueTest

friend class KisSimpleUpdateQueueTest
friend

Definition at line 175 of file kis_updater_context.h.

◆ KisStrokesQueueTest

friend class KisStrokesQueueTest
friend

Definition at line 174 of file kis_updater_context.h.

◆ KisUpdateJobItem

friend class KisUpdateJobItem
friend

Definition at line 176 of file kis_updater_context.h.

◆ KisUpdaterContextTest

friend class KisUpdaterContextTest
friend

Definition at line 172 of file kis_updater_context.h.

◆ KisUpdateSchedulerTest

friend class KisUpdateSchedulerTest
friend

Definition at line 173 of file kis_updater_context.h.

Member Data Documentation

◆ m_exclusiveJobLock

QReadWriteLock KisUpdaterContext::m_exclusiveJobLock
protected

The lock is shared by all the child update job items. When an item wants to run a usual (non-exclusive) job, it locks the lock for read access. When an exclusive access is requested, it locks it for write

Definition at line 158 of file kis_updater_context.h.

◆ m_jobs

QVector<KisUpdateJobItem*> KisUpdaterContext::m_jobs
protected

Definition at line 164 of file kis_updater_context.h.

◆ m_lock

QMutex KisUpdaterContext::m_lock
protected

Definition at line 160 of file kis_updater_context.h.

◆ m_lodCounter

KisLockFreeLodCounter KisUpdaterContext::m_lodCounter
protected

Definition at line 166 of file kis_updater_context.h.

◆ m_numRunningThreads

int KisUpdaterContext::m_numRunningThreads = 0
protected

Definition at line 162 of file kis_updater_context.h.

◆ m_runningThreadsMutex

QMutex KisUpdaterContext::m_runningThreadsMutex
mutableprotected

Definition at line 161 of file kis_updater_context.h.

◆ m_scheduler

KisUpdateScheduler* KisUpdaterContext::m_scheduler
protected

Definition at line 167 of file kis_updater_context.h.

◆ m_testingMode

bool KisUpdaterContext::m_testingMode = false
protected

Definition at line 168 of file kis_updater_context.h.

◆ m_threadPool

QThreadPool KisUpdaterContext::m_threadPool
protected

Definition at line 165 of file kis_updater_context.h.

◆ m_waitForDoneCondition

QWaitCondition KisUpdaterContext::m_waitForDoneCondition
protected

Definition at line 163 of file kis_updater_context.h.

◆ useIdealThreadCountTag

const int KisUpdaterContext::useIdealThreadCountTag = -1
static

Definition at line 30 of file kis_updater_context.h.


The documentation for this class was generated from the following files: