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

Fix deadlocks in KoShapeManager caused by bezier curve fix

1) Some shapes (e.g. Text Shape) may emit shape-changed signal right
   during painting. It would cause a deadlock.

2) add/removeShape methods should be more careful about locking
   because of their tail-recursion nature.

BUG:410909
BUG:410572
parent f54b48bb
......@@ -65,6 +65,8 @@ bool KoShapeManager::Private::shapeUsedInRenderingTree(KoShape *shape)
void KoShapeManager::Private::updateTree()
{
QMutexLocker l(&this->treeMutex);
// for detecting collisions between shapes.
DetectCollision detector;
bool selectionModified = false;
......@@ -166,14 +168,18 @@ KoShapeManager::~KoShapeManager()
void KoShapeManager::setShapes(const QList<KoShape *> &shapes, Repaint repaint)
{
QMutexLocker l(&d->mutex);
//clear selection
d->selection->deselectAll();
d->unlinkFromShapesRecursively(d->shapes);
d->aggregate4update.clear();
d->tree.clear();
d->shapes.clear();
{
QMutexLocker l1(&d->shapesMutex);
QMutexLocker l2(&d->treeMutex);
//clear selection
d->selection->deselectAll();
d->unlinkFromShapesRecursively(d->shapes);
d->aggregate4update.clear();
d->shapeIndexesBeforeUpdate.clear();
d->tree.clear();
d->shapes.clear();
}
Q_FOREACH (KoShape *shape, shapes) {
addShape(shape, repaint);
......@@ -182,16 +188,20 @@ void KoShapeManager::setShapes(const QList<KoShape *> &shapes, Repaint repaint)
void KoShapeManager::addShape(KoShape *shape, Repaint repaint)
{
QMutexLocker l(&d->mutex);
{
QMutexLocker l1(&d->shapesMutex);
if (d->shapes.contains(shape))
return;
shape->addShapeManager(this);
d->shapes.append(shape);
if (d->shapes.contains(shape))
return;
shape->addShapeManager(this);
d->shapes.append(shape);
if (d->shapeUsedInRenderingTree(shape)) {
QRectF br(shape->boundingRect());
d->tree.insert(br, shape);
if (d->shapeUsedInRenderingTree(shape)) {
QMutexLocker l2(&d->treeMutex);
QRectF br(shape->boundingRect());
d->tree.insert(br, shape);
}
}
if (repaint == PaintShapeOnAdd) {
......@@ -207,28 +217,35 @@ void KoShapeManager::addShape(KoShape *shape, Repaint repaint)
}
}
Private::DetectCollision detector;
detector.detect(d->tree, shape, shape->zIndex());
detector.fireSignals();
{
QMutexLocker l(&d->treeMutex);
Private::DetectCollision detector;
detector.detect(d->tree, shape, shape->zIndex());
detector.fireSignals();
}
}
void KoShapeManager::remove(KoShape *shape)
{
QMutexLocker l(&d->mutex);
{
QMutexLocker l1(&d->shapesMutex);
QMutexLocker l2(&d->treeMutex);
Private::DetectCollision detector;
detector.detect(d->tree, shape, shape->zIndex());
detector.fireSignals();
Private::DetectCollision detector;
detector.detect(d->tree, shape, shape->zIndex());
detector.fireSignals();
shape->update();
shape->removeShapeManager(this);
d->selection->deselect(shape);
d->aggregate4update.remove(shape);
shape->update();
shape->removeShapeManager(this);
d->selection->deselect(shape);
d->aggregate4update.remove(shape);
if (d->shapeUsedInRenderingTree(shape)) {
d->tree.remove(shape);
if (d->shapeUsedInRenderingTree(shape)) {
d->tree.remove(shape);
}
d->shapes.removeAll(shape);
}
d->shapes.removeAll(shape);
// remove the children of a KoShapeContainer
KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
......@@ -246,7 +263,8 @@ KoShapeManager::ShapeInterface::ShapeInterface(KoShapeManager *_q)
void KoShapeManager::ShapeInterface::notifyShapeDestructed(KoShape *shape)
{
QMutexLocker l(&q->d->mutex);
QMutexLocker l1(&q->d->shapesMutex);
QMutexLocker l2(&q->d->treeMutex);
q->d->selection->deselect(shape);
q->d->aggregate4update.remove(shape);
......@@ -269,7 +287,7 @@ KoShapeManager::ShapeInterface *KoShapeManager::shapeInterface()
void KoShapeManager::paint(QPainter &painter, const KoViewConverter &converter, bool forPrint)
{
QMutexLocker l(&d->mutex);
QMutexLocker l1(&d->shapesMutex);
d->updateTree();
painter.setPen(Qt::NoPen); // painters by default have a black stroke, lets turn that off.
......@@ -277,6 +295,8 @@ void KoShapeManager::paint(QPainter &painter, const KoViewConverter &converter,
QList<KoShape*> unsortedShapes;
if (painter.hasClipping()) {
QMutexLocker l(&d->treeMutex);
QRectF rect = converter.viewToDocument(KisPaintingTweaks::safeClipBoundingRect(painter));
unsortedShapes = d->tree.intersects(rect);
} else {
......@@ -503,10 +523,16 @@ void KoShapeManager::paintShape(KoShape *shape, QPainter &painter, const KoViewC
KoShape *KoShapeManager::shapeAt(const QPointF &position, KoFlake::ShapeSelection selection, bool omitHiddenShapes)
{
QMutexLocker l(&d->mutex);
QMutexLocker l(&d->shapesMutex);
d->updateTree();
QList<KoShape*> sortedShapes(d->tree.contains(position));
QList<KoShape*> sortedShapes;
{
QMutexLocker l(&d->treeMutex);
sortedShapes = d->tree.contains(position);
}
std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
KoShape *firstUnselectedShape = 0;
for (int count = sortedShapes.count() - 1; count >= 0; count--) {
......@@ -555,10 +581,15 @@ KoShape *KoShapeManager::shapeAt(const QPointF &position, KoFlake::ShapeSelectio
QList<KoShape *> KoShapeManager::shapesAt(const QRectF &rect, bool omitHiddenShapes, bool containedMode)
{
QMutexLocker l(&d->mutex);
QMutexLocker l(&d->shapesMutex);
d->updateTree();
QList<KoShape*> shapes(containedMode ? d->tree.contained(rect) : d->tree.intersects(rect));
QList<KoShape*> shapes;
{
QMutexLocker l(&d->treeMutex);
shapes = containedMode ? d->tree.contained(rect) : d->tree.intersects(rect);
}
for (int count = shapes.count() - 1; count >= 0; count--) {
......@@ -600,15 +631,17 @@ void KoShapeManager::update(const QRectF &rect, const KoShape *shape, bool selec
void KoShapeManager::notifyShapeChanged(KoShape *shape)
{
QMutexLocker l(&d->mutex);
{
QMutexLocker l(&d->treeMutex);
Q_ASSERT(shape);
if (d->aggregate4update.contains(shape)) {
return;
}
Q_ASSERT(shape);
if (d->aggregate4update.contains(shape)) {
return;
}
d->aggregate4update.insert(shape);
d->shapeIndexesBeforeUpdate.insert(shape, shape->zIndex());
d->aggregate4update.insert(shape);
d->shapeIndexesBeforeUpdate.insert(shape, shape->zIndex());
}
KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
if (container) {
......@@ -619,14 +652,14 @@ void KoShapeManager::notifyShapeChanged(KoShape *shape)
QList<KoShape*> KoShapeManager::shapes() const
{
QMutexLocker l(&d->mutex);
QMutexLocker l(&d->shapesMutex);
return d->shapes;
}
QList<KoShape*> KoShapeManager::topLevelShapes() const
{
QMutexLocker l(&d->mutex);
QMutexLocker l(&d->shapesMutex);
QList<KoShape*> shapes;
// get all toplevel shapes
......
......@@ -116,7 +116,8 @@ public:
QHash<KoShape*, int> shapeIndexesBeforeUpdate;
KoShapeManager *q;
KoShapeManager::ShapeInterface shapeInterface;
QMutex mutex;
QMutex shapesMutex;
QMutex treeMutex;
};
#endif
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