control: close directory watch on quick shutdown (fix SIGABRT at exit)

Control::handle_shutdown() closes m_directory_events (the inotify directory
watch) only in the normal-shutdown branch (!m_shutdownQuick). On a quick
shutdown (SIGTERM -> receive_quick_shutdown) the m_shutdownQuick branch skips
it, so the watch stays registered in the poll. ~Control() then destroys the
still-open directory_events and Event::~Event()'s assert(m_poll_event == nullptr)
(added with the 0.16.13 poll/event rework) aborts:

    main -> Control::~Control() -> ~directory_events -> ~Event() -> abort

Close it in Control::cleanup() instead, which runs on every shutdown path after
the session is saved; close() is idempotent, so the normal path that already
closed it in handle_shutdown() is unaffected.

Reproducible: configure a directory.watch.added watch, then SIGTERM the client
-> SIGABRT (core in Event::~Event); SIGINT (normal shutdown) is clean. With this
change SIGTERM exits cleanly too.
This commit is contained in:
Xirvik Support
2026-06-18 22:34:17 +00:00
committed by Jari Sundell
parent a79aaad9ab
commit 517454b413
+9
View File
@@ -88,6 +88,15 @@ Control::cleanup() {
if(!display::Canvas::daemon())
m_inputStdin->remove();
// Close the directory watch (inotify) Event on every shutdown path.
// handle_shutdown() only closes it on a normal shutdown; a quick shutdown
// (SIGTERM -> receive_quick_shutdown) takes the m_shutdownQuick branch and
// skips it, leaving directory_events registered in the poll. ~Control() would
// then destroy it while still open and trip the Event::~Event() assert
// (m_poll_event == nullptr) -> SIGABRT. close() is idempotent (no-op when the
// normal path already closed it).
m_directory_events->close();
if (scgi_thread::thread()->is_active())
scgi_thread::thread()->stop_thread_wait();