qt: wake the renderer CV before ghostty_surface_free in dtor

If the renderer thread is parked in presentVulkanDmabuf's
condition_variable wait (added in f48465cda) when GhosttySurface
is destroyed, ghostty_surface_free's per-surface tear-down race
with our mutex/CV destruction once the dtor body returns —
manifests as a SEGV (address boundary error) when fish kills the
parent shell and the surface dtors fan out faster than the
renderer can wake from its 100 ms timeout.

Set m_hidden=true + notify_all the compositor CV BEFORE handing
the surface to libghostty for tear-down. The renderer wakes
immediately, sees m_hidden, bails without parking, returns to
the xev loop, and libghostty's shutdown path joins the thread
cleanly while our mutex/CV are still alive.

Same pattern as the Hide / PlatformSurface destroy handlers
(which already notify_all for the same reason).

Co-Authored-By: claude-flow <ruv@ruv.net>
pull/12846/head
Nathan 2026-05-26 09:43:16 -05:00
parent f48465cda5
commit dab2a1930c
1 changed files with 16 additions and 0 deletions

View File

@ -214,6 +214,22 @@ GhosttySurface::~GhosttySurface() {
// QPointer auto-nulls on a destroyed QObject, so .data() is safe.
delete m_inspectorWindow.data();
// Wake the renderer thread if it's parked in presentVulkanDmabuf's
// CV wait BEFORE we hand the surface to libghostty for teardown.
// ghostty_surface_free below shuts down + joins the renderer
// thread; if that thread is blocked on our CV, the join either
// hangs for our 100 ms timeout (best case) or races our mutex /
// CV destruction once this body returns (worst case → SEGV when
// the renderer wakes from the timeout and touches the destroyed
// mutex). The predicate also checks m_hidden so the renderer
// bails out without parking another frame.
m_hidden.store(true, std::memory_order_release);
{
std::lock_guard<std::mutex> lg(m_compositorMutex);
m_compositorReady = true;
}
m_compositorCv.notify_all();
// GL teardown must happen with the context current. If makeCurrent
// fails (e.g. the ctor failed before m_context could be created), we
// still free m_surface — it carries no GL state of its own — and we