Forráskód Böngészése

Merge pull request #8592 from devsnek/sig-handlers

exit gracefully on sigint/sigterm
snek 4 éve
szülő
commit
9479940a1f
2 módosított fájl, 71 hozzáadás és 0 törlés
  1. 60 0
      src/yuzu/main.cpp
  2. 11 0
      src/yuzu/main.h

+ 60 - 0
src/yuzu/main.cpp

@@ -8,6 +8,10 @@
 #ifdef __APPLE__
 #include <unistd.h> // for chdir
 #endif
+#ifdef __linux__
+#include <csignal>
+#include <sys/socket.h>
+#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<Config>(*system)},
       vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
       provider{std::make_unique<FileSys::ManualContentProvider>()} {
+#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<int, 3> 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() {

+ 11 - 0
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<int, 3> sig_interrupt_fds;
+
     QDBusObjectPath wake_lock{};
 #endif