From 4d6b9fde2c46c9cd78ff086139704da4c33fa468 Mon Sep 17 00:00:00 2001 From: magdev Date: Sun, 3 May 2026 19:23:33 +0200 Subject: [PATCH] bundled: disconnect child signals before terminate() to prevent restart-during-shutdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit teardownChild called terminate() then waitForFinished(2000), then disconnected the QProcess signals. But waitForFinished pumps a local event loop — when frankenphp exited inside that wait, QProcess::finished fired synchronously, ran onChildFinished as the crash-supervisor's restart path, and spawned a brand-new frankenphp child during shutdown. That child's QProcess was then destroyed mid-spawn during stack unwinding, producing the "QProcess: Destroyed while process is still running" warning the bundled-supervisor.sh test catches. Fix: disconnect first, then terminate. Severing signals before the wait turns terminate() into the synchronous reap it should always have been; onChildFinished can't run for a process we're explicitly tearing down. Local integration test passes clean — both the cache-baked-mount-path relaunch and the graceful-shutdown assertion go through without the warning or any orphan frankenphp. Co-Authored-By: Claude Opus 4.7 (1M context) --- framework/qml/src/BackendConnection.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/framework/qml/src/BackendConnection.cpp b/framework/qml/src/BackendConnection.cpp index 8838689..981744b 100644 --- a/framework/qml/src/BackendConnection.cpp +++ b/framework/qml/src/BackendConnection.cpp @@ -380,6 +380,14 @@ bool BackendConnection::spawnChild(QString* errorOut) void BackendConnection::teardownChild() { if (!m_child) return; + // Disconnect *before* terminating: waitForFinished() pumps a local event + // loop, so QProcess::finished would fire synchronously inside that wait, + // run onChildFinished as the crash-supervisor restart path, and spawn a + // brand-new frankenphp child during shutdown — the new QProcess then + // gets destroyed mid-spawn during stack unwinding and Qt warns + // "Destroyed while process is still running". Severing signals first + // turns terminate() into the synchronous reap it should always have been. + disconnect(m_child, nullptr, this, nullptr); if (m_child->state() != QProcess::NotRunning) { m_child->terminate(); if (!m_child->waitForFinished(2000)) { @@ -387,7 +395,6 @@ void BackendConnection::teardownChild() m_child->waitForFinished(1000); } } - disconnect(m_child, nullptr, this, nullptr); m_child->deleteLater(); m_child = nullptr; m_childLogBuffer.clear();