Commit 62998489 authored by Dmitry Kazakov's avatar Dmitry Kazakov

Added a KisUpdaterContext class

This class is intended to do all the lowlevel work for threading. It
manages optimal number of threads and doesn't allow intersection of
simultaneously executing walkers.

svn path=/trunk/koffice/; revision=1133439
parent 966b6637
......@@ -125,6 +125,7 @@ set(kritaimage_LIB_SRCS
kis_gradient_job.cpp
kis_iterator_ng.cpp
kis_merge_walker.cc
kis_updater_context.cpp
kis_group_layer.cc
kis_count_visitor.cpp
kis_histogram.cc
......
......@@ -23,7 +23,10 @@
#include "kis_types.h"
#include "kis_base_rects_walker.h"
class KRITAIMAGE_EXPORT KisMergeWalker : public KisBaseRectsWalker
class KisMergeWalker;
typedef KisSharedPtr<KisMergeWalker> KisMergeWalkerSP;
class KRITAIMAGE_EXPORT KisMergeWalker : public KisBaseRectsWalker, public KisShared
{
public:
......
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_updater_context.h"
#include <QThread>
#include <QThreadPool>
KisUpdaterContext::KisUpdaterContext()
{
qint32 idealThreadCount = QThread::idealThreadCount();
idealThreadCount = idealThreadCount > 0 ? idealThreadCount : 1;
m_jobs.resize(idealThreadCount);
for(qint32 i = 0; i < m_jobs.size(); i++) {
m_jobs[i] = new KisUpdateJobItem();
connect(m_jobs[i], SIGNAL(sigJobFinished()),
SLOT(slotJobFinished()), Qt::DirectConnection);
}
}
KisUpdaterContext::~KisUpdaterContext()
{
m_threadPool.waitForDone();
for(qint32 i = 0; i < m_jobs.size(); i++)
delete m_jobs[i];
}
bool KisUpdaterContext::isJobAllowed(KisMergeWalkerSP walker)
{
bool intersects = false;
foreach(const KisUpdateJobItem *item, m_jobs) {
if(walkersIntersect(walker, item->walker())) {
intersects = true;
break;
}
}
return !intersects;
}
bool KisUpdaterContext::addJob(KisMergeWalkerSP walker)
{
qint32 jobIndex = findSpareThread();
if(jobIndex < 0) return false;
m_jobs[jobIndex]->setWalker(walker);
m_threadPool.start(m_jobs[jobIndex]);
return true;
}
/**
* This variant is for use in a testing suite only
*/
bool KisTestableUpdaterContext::addJob(KisMergeWalkerSP walker)
{
qint32 jobIndex = findSpareThread();
if(jobIndex < 0) return false;
m_jobs[jobIndex]->setWalker(walker);
// HINT: Not calling start() here
return true;
}
bool KisUpdaterContext::walkersIntersect(KisMergeWalkerSP walker1,
KisMergeWalkerSP walker2)
{
return (walker1->accessRect().intersects(walker2->changeRect())) ||
(walker2->accessRect().intersects(walker1->changeRect()));
}
qint32 KisUpdaterContext::findSpareThread()
{
for(qint32 i=0; i < m_jobs.size(); i++)
if(!m_jobs[i]->isRunning())
return i;
return -1;
}
void KisUpdaterContext::slotJobFinished()
{
// Be careful. This slot can be called asynchronously without locks.
emit wantSomeWork();
}
void KisUpdaterContext::lock()
{
m_lock.lock();
}
void KisUpdaterContext::unlock()
{
m_lock.unlock();
}
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_UPDATER_CONTEXT_H
#define __KIS_UPDATER_CONTEXT_H
#include <QMutex>
#include <QRunnable>
#include "kis_merge_walker.h"
#include "kis_async_merger.h"
class KisUpdateJobItem : public QObject, public QRunnable
{
Q_OBJECT
public:
KisUpdateJobItem() {
setAutoDelete(false);
}
void run() {
m_merger.startMerge(*m_walker);
setWalker(0);
emit sigJobFinished();
}
inline void setWalker(KisMergeWalkerSP walker) {
m_walker = walker;
}
inline KisMergeWalkerSP walker() const {
return m_walker;
}
inline bool isRunning() const {
return m_walker;
}
signals:
void sigJobFinished();
private:
KisMergeWalkerSP m_walker;
KisAsyncMerger m_merger;
};
class KRITAIMAGE_EXPORT KisUpdaterContext : public QObject
{
Q_OBJECT
public:
KisUpdaterContext();
~KisUpdaterContext();
/**
* 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 lock()
*/
bool isJobAllowed(KisMergeWalkerSP walker);
/**
* Registers the job and starts executing it.
* The caller must ensure that the context is locked
* with lock(), job is allowed with isWalkerAllowed()
*
* \see lock()
* \see isWalkerAllowed()
* \return true if the walker was successfully added and false
* if there is no spare thread present
*/
bool addJob(KisMergeWalkerSP walker);
/**
* Locks the context to guarantee an exclusive access
* to the context
*/
void lock();
/**
* Unlocks the context
*
* \see lock()
*/
void unlock();
signals:
void wantSomeWork();
protected slots:
void slotJobFinished();
protected:
static bool walkersIntersect(KisMergeWalkerSP walker1,
KisMergeWalkerSP walker2);
qint32 findSpareThread();
protected:
QMutex m_lock;
QVector<KisUpdateJobItem*> m_jobs;
QThreadPool m_threadPool;
};
class KRITAIMAGE_EXPORT KisTestableUpdaterContext : public KisUpdaterContext
{
public:
/**
* The only difference - it doesn't start execution
* of the job
*/
bool addJob(KisMergeWalkerSP walker);
};
#endif /* __KIS_UPDATER_CONTEXT_H */
......@@ -328,6 +328,12 @@ target_link_libraries(KisAsyncMergerTest ${KDE4_KDEUI_LIBS} kritaimage ${QT_QTT
########### next target ###############
set(kis_updater_context_test_SRCS kis_updater_context_test.cpp )
kde4_add_unit_test(KisUpdaterContextTest TESTNAME krita-image-KisUpdaterContextTest ${kis_updater_context_test_SRCS})
target_link_libraries(KisUpdaterContextTest ${KDE4_KDEUI_LIBS} kritaimage ${QT_QTTEST_LIBRARY})
########### next target ###############
set(kis_macro_test_SRCS kis_macro_test.cpp )
kde4_add_unit_test(KisMacroTest TESTNAME krita-image-KisMacroTest ${kis_macro_test_SRCS})
target_link_libraries(KisMacroTest ${KDE4_KDEUI_LIBS} kritaimage ${QT_QTTEST_LIBRARY})
......
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_updater_context_test.h"
#include <qtest_kde.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include "kis_updater_context.h"
void KisUpdaterContextTest::testJobInterference()
{
KisTestableUpdaterContext context;
QRect imageRect(0,0,100,100);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), cs, "merge test");
KisPaintLayerSP paintLayer = new KisPaintLayer(image, "test", OPACITY_OPAQUE_U8);
image->lock();
image->addNode(paintLayer);
QRect dirtyRect1(0,0,50,100);
KisMergeWalkerSP walker1 = new KisMergeWalker(imageRect);
walker1->collectRects(paintLayer, dirtyRect1);
QRect dirtyRect2(30,0,100,100);
KisMergeWalkerSP walker2 = new KisMergeWalker(imageRect);
walker2->collectRects(paintLayer, dirtyRect2);
context.lock();
context.addJob(walker1);
QVERIFY(!context.isJobAllowed(walker2));
context.unlock();
}
QTEST_KDEMAIN(KisUpdaterContextTest, NoGUI)
#include "kis_updater_context_test.moc"
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_UPDATER_CONTEXT_TEST_H
#define KIS_UPDATER_CONTEXT_TEST_H
#include <QtTest/QtTest>
class KisUpdaterContextTest : public QObject
{
Q_OBJECT
private slots:
void testJobInterference();
};
#endif /* KIS_UPDATER_CONTEXT_TEST_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment