diff --git a/autotests/client/test_wayland_shell.cpp b/autotests/client/test_wayland_shell.cpp --- a/autotests/client/test_wayland_shell.cpp +++ b/autotests/client/test_wayland_shell.cpp @@ -65,6 +65,7 @@ void testResize(); void testDisconnect(); void testWhileDestroying(); + void testClientDisconnecting(); private: KWayland::Server::Display *m_display; @@ -795,5 +796,46 @@ QVERIFY(clientErrorSpy.isEmpty()); } +void TestWaylandShell::testClientDisconnecting() +{ + // this test tries to request a new surface size while the client is actually already destroyed + // see BUG: 370232 + using namespace KWayland::Client; + using namespace KWayland::Server; + // create ShellSurface + QScopedPointer s(m_compositor->createSurface()); + QSignalSpy shellSurfaceCreatedSpy(m_shellInterface, &ShellInterface::surfaceCreated); + QVERIFY(shellSurfaceCreatedSpy.isValid()); + QScopedPointer ps(m_shell->createSurface(s.data())); + QVERIFY(shellSurfaceCreatedSpy.wait()); + + auto serverShellSurface = shellSurfaceCreatedSpy.first().first().value(); + QVERIFY(serverShellSurface); + + QSignalSpy shellSurfaceUnboundSpy(serverShellSurface, &Resource::unbound); + QVERIFY(shellSurfaceUnboundSpy.isValid()); + + QScopedPointer s2(m_compositor->createSurface()); + QScopedPointer ps2(m_shell->createSurface(s2.data())); + QVERIFY(shellSurfaceCreatedSpy.wait()); + auto serverShellSurface2 = shellSurfaceCreatedSpy.last().first().value(); + QVERIFY(serverShellSurface2); + + connect(serverShellSurface, &Resource::unbound, this, + [serverShellSurface, serverShellSurface2] { + serverShellSurface2->requestSize(QSize(100, 200)); + } + ); + + m_connection->deleteLater(); + m_connection = nullptr; + m_thread->quit(); + m_thread->wait(); + delete m_thread; + m_thread = nullptr; + + QVERIFY(shellSurfaceUnboundSpy.wait()); +} + QTEST_GUILESS_MAIN(TestWaylandShell) #include "test_wayland_shell.moc" diff --git a/autotests/server/test_display.cpp b/autotests/server/test_display.cpp --- a/autotests/server/test_display.cpp +++ b/autotests/server/test_display.cpp @@ -170,7 +170,10 @@ QVERIFY(disconnectedSpy.isEmpty()); wl_client_destroy(client); QCOMPARE(disconnectedSpy.count(), 1); + QSignalSpy clientDestroyedSpy(client2, &QObject::destroyed); + QVERIFY(clientDestroyedSpy.isValid()); client2->destroy(); + QVERIFY(clientDestroyedSpy.wait()); QCOMPARE(disconnectedSpy.count(), 2); close(sv[0]); close(sv[1]); diff --git a/src/server/clientconnection.cpp b/src/server/clientconnection.cpp --- a/src/server/clientconnection.cpp +++ b/src/server/clientconnection.cpp @@ -66,7 +66,9 @@ ClientConnection::Private::~Private() { - wl_list_remove(&listener.link); + if (client) { + wl_list_remove(&listener.link); + } s_allClients.removeAt(s_allClients.indexOf(this)); } @@ -80,9 +82,12 @@ } ); Q_ASSERT(it != s_allClients.constEnd()); - auto q = (*it)->q; + auto p = (*it); + auto q = p->q; + p->client = nullptr; + wl_list_remove(&p->listener.link); emit q->disconnected(q); - delete q; + q->deleteLater(); } ClientConnection::ClientConnection(wl_client *c, Display *parent) @@ -95,21 +100,33 @@ void ClientConnection::flush() { + if (!d->client) { + return; + } wl_client_flush(d->client); } void ClientConnection::destroy() { + if (!d->client) { + return; + } wl_client_destroy(d->client); } wl_resource *ClientConnection::createResource(const wl_interface *interface, quint32 version, quint32 id) { + if (!d->client) { + return nullptr; + } return wl_resource_create(d->client, interface, version, id); } wl_resource *ClientConnection::getResource(quint32 id) { + if (!d->client) { + return nullptr; + } return wl_client_get_object(d->client, id); }