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