Index: kdevplatform/shell/core.cpp =================================================================== --- kdevplatform/shell/core.cpp +++ kdevplatform/shell/core.cpp @@ -21,6 +21,7 @@ #include "core_p.h" #include +#include #include @@ -51,37 +52,63 @@ #include namespace { -void shutdownGracefully(int sig) -{ - static volatile std::sig_atomic_t handlingSignal = 0; - - if ( !handlingSignal ) { - handlingSignal = 1; - qCDebug(SHELL) << "signal " << sig << " received, shutting down gracefully"; +static KDevelop::CorePrivate *corePrivateInstance = nullptr; +static int signalPipeRead = -1; +static int signalPipeWrite = -1; +static QSocketNotifier* signalNotifier = nullptr; + +void shutdownGracefully() +{ + // we can use std::atomic here even if it is not lock_free + // because we are not running as a signal handler in the strict + // sense of the term. + static std::atomic handlingSignal(false); + + // Get the signal number that was written to the pipe in the actual + // signal handler. A bit awkward but we have to flush the read side + // of the pipe anyway. + // Failure to flush can lead to repetitive signals from QSocketNotifier + // as if it reacts to the presence of unread data rather than atomic + // writes to the pipe (observed on Linux 4.14 with Qt 5.9.6). + int sig; + read(signalPipeRead, &sig, sizeof(sig)); + + if (!handlingSignal.exchange(true)) { + // the first time we see a signal we attempt to exit + // as if the user initiated the procedure via the GUI. + qCWarning(SHELL) << "Going down on signal" << sig; QCoreApplication* app = QCoreApplication::instance(); if (QApplication* guiApp = qobject_cast(app)) { guiApp->closeAllWindows(); } app->quit(); return; } + qCWarning(SHELL) << "Going down harder on signal" << sig; - // re-raise signal with default handler and trigger program termination + // we come here if a 2nd signal was received before the full exit + // procedure completed, for instance because it blocked. std::signal(sig, SIG_DFL); + + signalNotifier->setEnabled(false); + + // re-raise signal with default handler and trigger program termination std::raise(sig); } +void signalHandler(int sig) +{ + if (signalPipeWrite != -1) { + write(signalPipeWrite, &sig, sizeof(sig)); + } +} + void installSignalHandler() { -#ifdef SIGHUP - std::signal(SIGHUP, shutdownGracefully); -#endif -#ifdef SIGINT - std::signal(SIGINT, shutdownGracefully); -#endif -#ifdef SIGTERM - std::signal(SIGTERM, shutdownGracefully); -#endif + std::signal(SIGHUP, signalHandler); + std::signal(SIGINT, signalHandler); + std::signal(SIGTERM, signalHandler); + signalNotifier->setEnabled(true); } } @@ -121,6 +148,7 @@ } CorePrivate::CorePrivate(Core *core): + QObject(nullptr), m_aboutData( createAboutData() ), m_core(core), m_cleanedUp(false), m_shuttingDown(false) { } @@ -289,7 +317,27 @@ testController->initialize(); runtimeController->initialize(); - installSignalHandler(); +// A "proper" exit-on-signal approach: +// Open a pipe or an eventfd, then install your signal handler. In that signal +// handler, write anything to the writing end or write uint64_t(1) the eventfd. +// Create a QSocketNotifier on the reading end of the pipe or on the eventfd, +// connect its activation signal to a slot that does what you want. + +#ifdef Q_OS_UNIX + if (!corePrivateInstance) { + int pp[2]; + if (pipe(pp)) { + qCWarning(SHELL) << "Error opening signal handler pipe" << strerror(errno); + } else { + signalPipeRead = pp[0]; + signalPipeWrite = pp[1]; + signalNotifier = new QSocketNotifier(signalPipeRead, QSocketNotifier::Read, this); + connect(signalNotifier, &QSocketNotifier::activated, this, &CorePrivate::shutdownGracefully, Qt::DirectConnection); + installSignalHandler(); + } + corePrivateInstance = this; + } +#endif qCDebug(SHELL) << "Done initializing controllers"; @@ -328,8 +376,23 @@ workingSetController.clear(); testController.clear(); runtimeController.clear(); + +#ifdef USE_QSOCKETNOTIFIER + if (signalPipeWrite != -1) { + close(signalPipeWrite); + } + if (signalPipeRead != -1) { + close(signalPipeRead); + } +#endif +} + +void CorePrivate::shutdownGracefully() +{ + ::shutdownGracefully(); } + bool Core::initialize(Setup mode, const QString& session) { if (m_self) Index: kdevplatform/shell/core_p.h =================================================================== --- kdevplatform/shell/core_p.h +++ kdevplatform/shell/core_p.h @@ -47,11 +47,15 @@ class TestController; class RuntimeController; -class KDEVPLATFORMSHELL_EXPORT CorePrivate { +class KDEVPLATFORMSHELL_EXPORT CorePrivate : public QObject { + Q_OBJECT + public: explicit CorePrivate(Core *core); ~CorePrivate(); bool initialize( Core::Setup mode, const QString& session ); + void shutdownGracefully(); + QPointer pluginController; QPointer uiController; QPointer projectController;