From 9479940a1fc7a399875d07496e036ce714b51207 Mon Sep 17 00:00:00 2001 From: snek Date: Wed, 27 Jul 2022 12:51:26 -0700 Subject: [PATCH] Merge pull request #8592 from devsnek/sig-handlers exit gracefully on sigint/sigterm --- src/yuzu/main.cpp | 60 +++++++++++++++++++++++++++++++++++++++++++++++ src/yuzu/main.h | 11 +++++++++ 2 files changed, 71 insertions(+) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 84fccab346..ef91ef19c5 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -8,6 +8,10 @@ #ifdef __APPLE__ #include // for chdir #endif +#ifdef __linux__ +#include +#include +#endif // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. #include "applets/qt_controller.h" @@ -259,6 +263,10 @@ GMainWindow::GMainWindow(bool has_broken_vulkan) config{std::make_unique(*system)}, vfs{std::make_shared()}, provider{std::make_unique()} { +#ifdef __linux__ + SetupSigInterrupts(); +#endif + Common::Log::Initialize(); LoadTranslation(); @@ -462,7 +470,13 @@ GMainWindow::~GMainWindow() { if (render_window->parent() == nullptr) { delete render_window; } + system->GetRoomNetwork().Shutdown(); + +#ifdef __linux__ + ::close(sig_interrupt_fds[0]); + ::close(sig_interrupt_fds[1]); +#endif } void GMainWindow::RegisterMetaTypes() { @@ -1352,6 +1366,52 @@ static void ReleaseWakeLockLinux(QDBusObjectPath lock) { QString::fromLatin1("org.freedesktop.portal.Request")); unlocker.call(QString::fromLatin1("Close")); } + +std::array GMainWindow::sig_interrupt_fds{0, 0, 0}; + +void GMainWindow::SetupSigInterrupts() { + if (sig_interrupt_fds[2] == 1) { + return; + } + socketpair(AF_UNIX, SOCK_STREAM, 0, sig_interrupt_fds.data()); + sig_interrupt_fds[2] = 1; + + struct sigaction sa; + sa.sa_handler = &GMainWindow::HandleSigInterrupt; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESETHAND; + sigaction(SIGINT, &sa, nullptr); + sigaction(SIGTERM, &sa, nullptr); + + sig_interrupt_notifier = new QSocketNotifier(sig_interrupt_fds[1], QSocketNotifier::Read, this); + connect(sig_interrupt_notifier, &QSocketNotifier::activated, this, + &GMainWindow::OnSigInterruptNotifierActivated); + connect(this, &GMainWindow::SigInterrupt, this, &GMainWindow::close); +} + +void GMainWindow::HandleSigInterrupt(int sig) { + if (sig == SIGINT) { + exit(1); + } + + // Calling into Qt directly from a signal handler is not safe, + // so wake up a QSocketNotifier with this hacky write call instead. + char a = 1; + int ret = write(sig_interrupt_fds[0], &a, sizeof(a)); + (void)ret; +} + +void GMainWindow::OnSigInterruptNotifierActivated() { + sig_interrupt_notifier->setEnabled(false); + + char a; + int ret = read(sig_interrupt_fds[1], &a, sizeof(a)); + (void)ret; + + sig_interrupt_notifier->setEnabled(true); + + emit SigInterrupt(); +} #endif // __linux__ void GMainWindow::PreventOSSleep() { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index de0d109744..509bb91dfe 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -163,6 +163,8 @@ signals: void WebBrowserExtractOfflineRomFS(); void WebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason, std::string last_url); + void SigInterrupt(); + public slots: void OnLoadComplete(); void OnExecuteProgram(std::size_t program_index); @@ -251,6 +253,12 @@ private: void RequestGameResume(); void closeEvent(QCloseEvent* event) override; +#ifdef __linux__ + void SetupSigInterrupts(); + static void HandleSigInterrupt(int); + void OnSigInterruptNotifierActivated(); +#endif + private slots: void OnStartGame(); void OnRestartGame(); @@ -419,6 +427,9 @@ private: bool is_tas_recording_dialog_active{}; #ifdef __linux__ + QSocketNotifier* sig_interrupt_notifier; + static std::array sig_interrupt_fds; + QDBusObjectPath wake_lock{}; #endif