feat: gracefully shut down on SIGINT/SIGTERM on Linux/mac (#6053)
This commit is contained in:
@@ -44,6 +44,7 @@
|
||||
- Dev: Subscriptions to PubSub channel points redemption topics now use no auth token, making it continue to work during PubSub shutdown. (#5947)
|
||||
- Dev: Add initial experimental EventSub support. (#5837, #5895, #5897, #5904, #5910, #5903, #5915, #5916, #5930, #5935, #5932, #5943, #5952, #5953, #5968, #5973, #5974, #5980, #5981, #5985, #5990, #5992, #5993, #5996, #5995, #6000, #6001, #6002, #6003, #6005, #6007, #6010, #6008, #6012, #6013, #6015, #6017, #6027, #6028, #6035, #6036, #6040, #6041, #6048)
|
||||
- Dev: Remove unneeded platform specifier for toasts. (#5914)
|
||||
- Dev: Cleanly shutdown on `SIGINT`/`SIGTERM` on Linux & macOS. (#6053)
|
||||
- Dev: Highlight checks now use non-capturing groups for the boundaries. (#5784)
|
||||
- Dev: Removed unused PubSub whisper code. (#5898)
|
||||
- Dev: Updated Conan dependencies. (#5776)
|
||||
|
||||
@@ -547,6 +547,8 @@ set(SOURCE_FILES
|
||||
util/Twitch.cpp
|
||||
util/Twitch.hpp
|
||||
util/TypeName.hpp
|
||||
util/UnixSignalHandler.cpp
|
||||
util/UnixSignalHandler.hpp
|
||||
util/Variant.hpp
|
||||
util/VectorMessageSink.cpp
|
||||
util/VectorMessageSink.hpp
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "singletons/Settings.hpp"
|
||||
#include "singletons/Updates.hpp"
|
||||
#include "util/CombinePath.hpp"
|
||||
#include "util/UnixSignalHandler.hpp"
|
||||
#include "widgets/dialogs/LastRunCrashDialog.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
@@ -172,6 +173,21 @@ namespace {
|
||||
|
||||
signal(SIGSEGV, handleSignal);
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_UNIX)
|
||||
auto *sigintHandler = new UnixSignalHandler(SIGINT);
|
||||
QObject::connect(sigintHandler, &UnixSignalHandler::signalFired, [] {
|
||||
qCInfo(chatterinoApp)
|
||||
<< "Received SIGINT, request application quit";
|
||||
QApplication::quit();
|
||||
});
|
||||
auto *sigtermHandler = new UnixSignalHandler(SIGTERM);
|
||||
QObject::connect(sigtermHandler, &UnixSignalHandler::signalFired, [] {
|
||||
qCInfo(chatterinoApp)
|
||||
<< "Received SIGTERM, request application quit";
|
||||
QApplication::quit();
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
// We delete cache files that haven't been modified in 14 days. This strategy may be
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
#include <QtGlobal>
|
||||
|
||||
#if defined(Q_OS_UNIX)
|
||||
|
||||
# include "common/QLogging.hpp"
|
||||
# include "util/UnixSignalHandler.hpp"
|
||||
|
||||
# include <QPointer>
|
||||
# include <sys/socket.h>
|
||||
# include <unistd.h>
|
||||
|
||||
# include <csignal>
|
||||
# include <unordered_map>
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace chatterino;
|
||||
|
||||
std::unordered_map<int, QPointer<UnixSignalHandler>> HANDLERS{};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
UnixSignalHandler::UnixSignalHandler(int signal)
|
||||
{
|
||||
if (::socketpair(AF_UNIX, SOCK_STREAM, 0, this->fd.data()) != 0)
|
||||
{
|
||||
qCWarning(chatterinoApp)
|
||||
<< "Failed to create UnixSignalHandler for" << signal;
|
||||
return;
|
||||
}
|
||||
|
||||
this->socketNotifier =
|
||||
new QSocketNotifier(this->fd[1], QSocketNotifier::Read, this);
|
||||
|
||||
QObject::connect(this->socketNotifier, &QSocketNotifier::activated, this,
|
||||
&UnixSignalHandler::handleSignal);
|
||||
|
||||
struct sigaction action;
|
||||
action.sa_handler = UnixSignalHandler::fired;
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_flags = 0;
|
||||
action.sa_flags |= SA_RESTART;
|
||||
|
||||
if (sigaction(signal, &action, nullptr) != 0)
|
||||
{
|
||||
qCWarning(chatterinoApp) << "Failed to listen to signal for" << signal;
|
||||
return;
|
||||
}
|
||||
|
||||
HANDLERS[signal] = QPointer(this);
|
||||
}
|
||||
|
||||
void UnixSignalHandler::fired(int signal)
|
||||
{
|
||||
const auto &cls = HANDLERS[signal];
|
||||
assert(!cls.isNull());
|
||||
if (cls.isNull())
|
||||
{
|
||||
qCWarning(chatterinoApp)
|
||||
<< "unix signal fired without a registered handler";
|
||||
return;
|
||||
}
|
||||
|
||||
char a = 1;
|
||||
::write(cls->fd[0], &a, sizeof(a));
|
||||
}
|
||||
|
||||
void UnixSignalHandler::handleSignal()
|
||||
{
|
||||
this->socketNotifier->setEnabled(false);
|
||||
|
||||
char tmp{};
|
||||
::read(this->fd[1], &tmp, sizeof(tmp));
|
||||
|
||||
this->socketNotifier->setEnabled(true);
|
||||
|
||||
this->signalFired();
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
#endif
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#if defined(Q_OS_UNIX)
|
||||
# include <QObject>
|
||||
# include <QSocketNotifier>
|
||||
|
||||
# include <array>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class UnixSignalHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
UnixSignalHandler(int signal);
|
||||
|
||||
static void fired(int signal);
|
||||
|
||||
Q_SIGNALS:
|
||||
void signalFired();
|
||||
|
||||
private:
|
||||
void handleSignal();
|
||||
|
||||
std::array<int, 2> fd{};
|
||||
QSocketNotifier *socketNotifier;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user