Commit 1e5db24a authored by Dmitry Kazakov's avatar Dmitry Kazakov

Fix crash when creating a bezier curve

The patch basically makes KoShapeManager thread safe by adding
a simple mutex. The problem is that both,
KoCreatePathTool::Private::endPointAtPosition() and
KisRepaintShapeLayerLayerJob access the shape manager in different
threads concurrently, which obviously causes a crash.

BUG:410572
BACKPORT:krita/4.2
parent a5ef0656
......@@ -166,6 +166,8 @@ KoShapeManager::~KoShapeManager()
void KoShapeManager::setShapes(const QList<KoShape *> &shapes, Repaint repaint)
{
QMutexLocker l(&d->mutex);
//clear selection
d->selection->deselectAll();
d->unlinkFromShapesRecursively(d->shapes);
......@@ -180,6 +182,8 @@ void KoShapeManager::setShapes(const QList<KoShape *> &shapes, Repaint repaint)
void KoShapeManager::addShape(KoShape *shape, Repaint repaint)
{
QMutexLocker l(&d->mutex);
if (d->shapes.contains(shape))
return;
shape->addShapeManager(this);
......@@ -210,6 +214,8 @@ void KoShapeManager::addShape(KoShape *shape, Repaint repaint)
void KoShapeManager::remove(KoShape *shape)
{
QMutexLocker l(&d->mutex);
Private::DetectCollision detector;
detector.detect(d->tree, shape, shape->zIndex());
detector.fireSignals();
......@@ -240,6 +246,8 @@ KoShapeManager::ShapeInterface::ShapeInterface(KoShapeManager *_q)
void KoShapeManager::ShapeInterface::notifyShapeDestructed(KoShape *shape)
{
QMutexLocker l(&q->d->mutex);
q->d->selection->deselect(shape);
q->d->aggregate4update.remove(shape);
......@@ -261,6 +269,8 @@ KoShapeManager::ShapeInterface *KoShapeManager::shapeInterface()
void KoShapeManager::paint(QPainter &painter, const KoViewConverter &converter, bool forPrint)
{
QMutexLocker l(&d->mutex);
d->updateTree();
painter.setPen(Qt::NoPen); // painters by default have a black stroke, lets turn that off.
painter.setBrush(Qt::NoBrush);
......@@ -493,6 +503,8 @@ void KoShapeManager::paintShape(KoShape *shape, QPainter &painter, const KoViewC
KoShape *KoShapeManager::shapeAt(const QPointF &position, KoFlake::ShapeSelection selection, bool omitHiddenShapes)
{
QMutexLocker l(&d->mutex);
d->updateTree();
QList<KoShape*> sortedShapes(d->tree.contains(position));
std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
......@@ -543,6 +555,8 @@ KoShape *KoShapeManager::shapeAt(const QPointF &position, KoFlake::ShapeSelectio
QList<KoShape *> KoShapeManager::shapesAt(const QRectF &rect, bool omitHiddenShapes, bool containedMode)
{
QMutexLocker l(&d->mutex);
d->updateTree();
QList<KoShape*> shapes(containedMode ? d->tree.contained(rect) : d->tree.intersects(rect));
......@@ -575,6 +589,8 @@ QList<KoShape *> KoShapeManager::shapesAt(const QRectF &rect, bool omitHiddenSha
void KoShapeManager::update(const QRectF &rect, const KoShape *shape, bool selectionHandles)
{
// TODO: do we need locking here?
d->canvas->updateCanvas(rect);
if (selectionHandles && d->selection->isSelected(shape)) {
if (d->canvas->toolProxy())
......@@ -584,6 +600,8 @@ void KoShapeManager::update(const QRectF &rect, const KoShape *shape, bool selec
void KoShapeManager::notifyShapeChanged(KoShape *shape)
{
QMutexLocker l(&d->mutex);
Q_ASSERT(shape);
if (d->aggregate4update.contains(shape)) {
return;
......@@ -601,11 +619,15 @@ void KoShapeManager::notifyShapeChanged(KoShape *shape)
QList<KoShape*> KoShapeManager::shapes() const
{
QMutexLocker l(&d->mutex);
return d->shapes;
}
QList<KoShape*> KoShapeManager::topLevelShapes() const
{
QMutexLocker l(&d->mutex);
QList<KoShape*> shapes;
// get all toplevel shapes
Q_FOREACH (KoShape *shape, d->shapes) {
......
......@@ -28,6 +28,7 @@
#include "KoShapeContainer.h"
#include "KoShapeManager.h"
#include <KoRTree.h>
#include <QMutex>
class KoCanvasBase;
......@@ -115,6 +116,7 @@ public:
QHash<KoShape*, int> shapeIndexesBeforeUpdate;
KoShapeManager *q;
KoShapeManager::ShapeInterface shapeInterface;
QMutex mutex;
};
#endif
......@@ -224,7 +224,7 @@ void KisToolShape::addShape(KoShape* shape)
parentCommand->setText(cmd->text());
new KoKeepShapesSelectedCommand(oldSelectedShapes, {shape}, canvas()->selectedShapesProxy(), true, parentCommand);
KisProcessingApplicator::runSingleCommandStroke(image(), cmd);
KisProcessingApplicator::runSingleCommandStroke(image(), cmd, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
}
void KisToolShape::addPathShape(KoPathShape* pathShape, const KUndo2MagicString& name)
......
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