0
0
mirror of https://github.com/obsproject/obs-studio.git synced 2024-09-20 04:42:18 +02:00

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
This commit is contained in:
Richard Stanway 2023-03-22 00:07:15 +01:00 committed by Jim
parent b79363400c
commit 5182368009
2 changed files with 53 additions and 3 deletions

View File

@ -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;

View File

@ -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();