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 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 275 of file kis_updater_context.cpp.

276{
278}
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 230 of file kis_updater_context.cpp.

231{
232 for(qint32 i=0; i < m_jobs.size(); i++)
233 if(!m_jobs[i]->isRunning())
234 return i;
235
236 return -1;
237}

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 307 of file kis_updater_context.cpp.

308{
309 return m_jobs;
310}

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.

◆ 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 291 of file kis_updater_context.cpp.

292{
293 QMutexLocker l(&m_runningThreadsMutex);
296
297 if (m_numRunningThreads <= 0) {
298 m_waitForDoneCondition.wakeAll();
299 }
300}
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 239 of file kis_updater_context.cpp.

240{
241 m_lock.lock();
242}

References m_lock.

◆ setTestingMode()

void KisUpdaterContext::setTestingMode ( bool value)

Definition at line 302 of file kis_updater_context.cpp.

303{
305}
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 249 of file kis_updater_context.cpp.

250{
251 m_threadPool.setMaxThreadCount(value);
252
253 for (int i = 0; i < m_jobs.size(); i++) {
254 KIS_SAFE_ASSERT_RECOVER_RETURN(!m_jobs[i]->isRunning());
255 // don't delete the jobs until all of them are checked!
256 }
257
258 for (int i = 0; i < m_jobs.size(); i++) {
259 delete m_jobs[i];
260 }
261
262 m_jobs.resize(value);
263
264 for(qint32 i = 0; i < m_jobs.size(); i++) {
265 m_jobs[i] = new KisUpdateJobItem(this);
266 }
267}
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 269 of file kis_updater_context.cpp.

270{
271 KIS_SAFE_ASSERT_RECOVER_NOOP(m_jobs.size() == m_threadPool.maxThreadCount());
272 return m_jobs.size();
273}

References KIS_SAFE_ASSERT_RECOVER_NOOP, m_jobs, and m_threadPool.

◆ unlock()

void KisUpdaterContext::unlock ( )

Unlocks the context

See also
lock()

Definition at line 244 of file kis_updater_context.cpp.

245{
246 m_lock.unlock();
247}

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 218 of file kis_updater_context.cpp.

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

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

Friends And Related Symbol Documentation

◆ KisSimpleUpdateQueueTest

friend class KisSimpleUpdateQueueTest
friend

Definition at line 166 of file kis_updater_context.h.

◆ KisStrokesQueueTest

friend class KisStrokesQueueTest
friend

Definition at line 165 of file kis_updater_context.h.

◆ KisUpdateJobItem

friend class KisUpdateJobItem
friend

Definition at line 167 of file kis_updater_context.h.

◆ KisUpdaterContextTest

friend class KisUpdaterContextTest
friend

Definition at line 163 of file kis_updater_context.h.

◆ KisUpdateSchedulerTest

friend class KisUpdateSchedulerTest
friend

Definition at line 164 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 149 of file kis_updater_context.h.

◆ m_jobs

QVector<KisUpdateJobItem*> KisUpdaterContext::m_jobs
protected

Definition at line 155 of file kis_updater_context.h.

◆ m_lock

QMutex KisUpdaterContext::m_lock
protected

Definition at line 151 of file kis_updater_context.h.

◆ m_lodCounter

KisLockFreeLodCounter KisUpdaterContext::m_lodCounter
protected

Definition at line 157 of file kis_updater_context.h.

◆ m_numRunningThreads

int KisUpdaterContext::m_numRunningThreads = 0
protected

Definition at line 153 of file kis_updater_context.h.

◆ m_runningThreadsMutex

QMutex KisUpdaterContext::m_runningThreadsMutex
protected

Definition at line 152 of file kis_updater_context.h.

◆ m_scheduler

KisUpdateScheduler* KisUpdaterContext::m_scheduler
protected

Definition at line 158 of file kis_updater_context.h.

◆ m_testingMode

bool KisUpdaterContext::m_testingMode = false
protected

Definition at line 159 of file kis_updater_context.h.

◆ m_threadPool

QThreadPool KisUpdaterContext::m_threadPool
protected

Definition at line 156 of file kis_updater_context.h.

◆ m_waitForDoneCondition

QWaitCondition KisUpdaterContext::m_waitForDoneCondition
protected

Definition at line 154 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: