فهرست منبع

UI: Properly handle SIGINT on non-Windows platforms

Running Qt code in a signal handler is not safe. Per the Qt docs, we
instead create a pair of sockets and a QSocketNotifier so that the
signal handler can notify the main thread which can process the exit
request without risk.

Reference: https://doc.qt.io/archives/qt-6.3/unix-signals.html
Reference: https://man7.org/linux/man-pages/man7/signal-safety.7.html
Richard Stanway 2 سال پیش
والد
کامیت
5182368009
2فایلهای تغییر یافته به همراه53 افزوده شده و 3 حذف شده
  1. 41 3
      UI/obs-app.cpp
  2. 12 0
      UI/obs-app.hpp

+ 41 - 3
UI/obs-app.cpp

@@ -59,6 +59,9 @@
 #else
 #include <signal.h>
 #include <pthread.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
 #endif
 
 #if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER)
@@ -108,6 +111,10 @@ bool restart = false;
 
 QPointer<OBSLogViewer> obsLogViewer;
 
+#ifndef _WIN32
+int OBSApp::sigintFd[2];
+#endif
+
 // GPU hint exports for AMD/NVIDIA laptops
 #ifdef _MSC_VER
 extern "C" __declspec(dllexport) DWORD NvOptimusEnablement = 1;
@@ -1391,6 +1398,14 @@ OBSApp::OBSApp(int &argc, char **argv, profiler_name_store_t *store)
 		blog(LOG_WARNING, "Failed to set LC_NUMERIC to C locale");
 #endif
 
+#ifndef _WIN32
+	/* Handle SIGINT properly */
+	socketpair(AF_UNIX, SOCK_STREAM, 0, sigintFd);
+	snInt = new QSocketNotifier(sigintFd[1], QSocketNotifier::Read, this);
+	connect(snInt, SIGNAL(activated(QSocketDescriptor)), this,
+		SLOT(ProcessSigInt()));
+#endif
+
 	sleepInhibitor = os_inhibit_sleep_create("OBS Video/audio");
 
 #ifndef __APPLE__
@@ -1407,6 +1422,10 @@ OBSApp::~OBSApp()
 		config_get_bool(globalConfig, "Audio", "DisableAudioDucking");
 	if (disableAudioDucking)
 		DisableAudioDucking(false);
+#else
+	delete snInt;
+	close(sigintFd[0]);
+	close(sigintFd[1]);
 #endif
 
 #ifdef __APPLE__
@@ -3176,12 +3195,31 @@ static void upgrade_settings(void)
 	os_closedir(dir);
 }
 
-void ctrlc_handler(int s)
+#ifndef _WIN32
+void OBSApp::SigIntSignalHandler(int s)
 {
+	/* Handles SIGINT and writes to a socket. Qt will read
+	 * from the socket in the main thread event loop and trigger
+	 * a call to the ProcessSigInt slot, where we can safely run
+	 * shutdown code without signal safety issues. */
 	UNUSED_PARAMETER(s);
 
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
+	char a = 1;
+	send(sigintFd[0], &a, sizeof(a), 0);
+}
+#endif
+
+void OBSApp::ProcessSigInt(void)
+{
+	/* This looks weird, but we can't ifdef a Qt slot function so
+	 * the SIGINT handler simply does nothing on Windows. */
+#ifndef _WIN32
+	char tmp;
+	recv(sigintFd[1], &tmp, sizeof(tmp), 0);
+
+	OBSBasic *main = reinterpret_cast<OBSBasic *>(GetMainWindow());
 	main->close();
+#endif
 }
 
 int main(int argc, char *argv[])
@@ -3191,7 +3229,7 @@ int main(int argc, char *argv[])
 
 	struct sigaction sig_handler;
 
-	sig_handler.sa_handler = ctrlc_handler;
+	sig_handler.sa_handler = OBSApp::SigIntSignalHandler;
 	sigemptyset(&sig_handler.sa_mask);
 	sig_handler.sa_flags = 0;
 

+ 12 - 0
UI/obs-app.hpp

@@ -20,6 +20,9 @@
 #include <QApplication>
 #include <QTranslator>
 #include <QPointer>
+#ifndef _WIN32
+#include <QSocketNotifier>
+#endif
 #include <obs.hpp>
 #include <util/lexer.h>
 #include <util/profiler.h>
@@ -125,6 +128,11 @@ private:
 
 	bool notify(QObject *receiver, QEvent *e) override;
 
+#ifndef _WIN32
+	static int sigintFd[2];
+	QSocketNotifier *snInt = nullptr;
+#endif
+
 public:
 	OBSApp(int &argc, char **argv, profiler_name_store_t *store);
 	~OBSApp();
@@ -208,9 +216,13 @@ public:
 	}
 
 	inline void PopUITranslation() { translatorHooks.pop_front(); }
+#ifndef _WIN32
+	static void SigIntSignalHandler(int);
+#endif
 
 public slots:
 	void Exec(VoidFunc func);
+	void ProcessSigInt();
 
 signals:
 	void StyleChanged();