Delay syncing internal window geometry to end of cycle

Summary:
The syncing of the window geometry to the internal geometry can
unfortunately cause a freeze in very special conditions:
1. create QML component
2. a Plasma::Dialog gets created
3. It creates the DialogShadows
4. This triggers QGlobalStatic creation which locks a non-recursive
mutex
5. The creation of DialogShadows creates a Registry and triggers a
roundtrip on the Wayland server
6. KWin processes all Wayland events
7. This triggers the creation of a ShellClient
8. The ShellClient has a PlasmaShellSurface which requested a position
9. The new geometry does not match the geometry of the Plasma::Dialog
10. ShellClient syncs the geometry to the Plasma::Dialog
11. Plasma::Dialog updates the theme because window geometry changed
12. This accesses the DialogShadows...

which is still in the non recursive mutex and we have a freeze.

By delaying the sync to the end of cycle we jump out of this deadly
sequence.

BUG: 384441

Test Plan:
The freeze doesn't hit any more. It's possible that some test
cases need adjustments.

Reviewers: #kwin, #plasma

Subscribers: plasma-devel, kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D7712
This commit is contained in:
Martin Flöser 2017-09-06 20:45:51 +02:00
parent d1e0c6f9d7
commit e0f95fd913
2 changed files with 5 additions and 4 deletions

View file

@ -538,11 +538,11 @@ void InternalWindowTest::testMove()
// normal move should be synced // normal move should be synced
internalClient->move(5, 10); internalClient->move(5, 10);
QCOMPARE(internalClient->geometry(), QRect(5, 10, 100, 100)); QCOMPARE(internalClient->geometry(), QRect(5, 10, 100, 100));
QCOMPARE(win.geometry(), QRect(5, 10, 100, 100)); QTRY_COMPARE(win.geometry(), QRect(5, 10, 100, 100));
// another move should also be synced // another move should also be synced
internalClient->move(10, 20); internalClient->move(10, 20);
QCOMPARE(internalClient->geometry(), QRect(10, 20, 100, 100)); QCOMPARE(internalClient->geometry(), QRect(10, 20, 100, 100));
QCOMPARE(win.geometry(), QRect(10, 20, 100, 100)); QTRY_COMPARE(win.geometry(), QRect(10, 20, 100, 100));
// now move with a Geometry update blocker // now move with a Geometry update blocker
{ {
@ -552,7 +552,7 @@ void InternalWindowTest::testMove()
QCOMPARE(win.geometry(), QRect(10, 20, 100, 100)); QCOMPARE(win.geometry(), QRect(10, 20, 100, 100));
} }
// after destroying the blocker it should be synced // after destroying the blocker it should be synced
QCOMPARE(win.geometry(), QRect(5, 10, 100, 100)); QTRY_COMPARE(win.geometry(), QRect(5, 10, 100, 100));
} }
void InternalWindowTest::testSkipCloseAnimation_data() void InternalWindowTest::testSkipCloseAnimation_data()

View file

@ -543,7 +543,8 @@ void ShellClient::syncGeometryToInternalWindow()
const QRect windowRect = QRect(geom.topLeft() + QPoint(borderLeft(), borderTop()), const QRect windowRect = QRect(geom.topLeft() + QPoint(borderLeft(), borderTop()),
geom.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom())); geom.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom()));
if (m_internalWindow->geometry() != windowRect) { if (m_internalWindow->geometry() != windowRect) {
m_internalWindow->setGeometry(windowRect); // delay to end of cycle to prevent freeze, see BUG 384441
QTimer::singleShot(0, m_internalWindow, std::bind(static_cast<void (QWindow::*)(const QRect&)>(&QWindow::setGeometry), m_internalWindow, windowRect));
} }
} }