From b60c3cab7b8646d3bd76a7083c26d6372505ecc4 Mon Sep 17 00:00:00 2001 From: Peter L Jones Date: Fri, 8 May 2020 17:16:27 +0100 Subject: [PATCH 1/2] Handle server application closure more cleanly --- Jamulus.pro | 4 +- src/client.cpp | 2 +- src/recorder/jamrecorder.cpp | 44 +++++++---- src/recorder/jamrecorder.h | 22 ++++-- src/server.cpp | 61 ++++++++++++++- src/server.h | 9 +++ src/serverdlg.cpp | 12 --- src/serverdlg.h | 1 - src/signalhandler.cpp | 146 +++++++++++++++++++++++++++++++++++ src/signalhandler.h | 140 +++++++++++++++++++++++++++++++++ 10 files changed, 403 insertions(+), 38 deletions(-) create mode 100755 src/signalhandler.cpp create mode 100755 src/signalhandler.h diff --git a/Jamulus.pro b/Jamulus.pro index b517c99b..1421d443 100755 --- a/Jamulus.pro +++ b/Jamulus.pro @@ -210,7 +210,8 @@ HEADERS += src/audiomixerboard.h \ src/recorder/jamrecorder.h \ src/recorder/creaperproject.h \ src/recorder/cwavestream.h \ - src/historygraph.h + src/historygraph.h \ + src/signalhandler.h HEADERS_OPUS = libs/opus/celt/arch.h \ libs/opus/celt/bands.h \ @@ -324,6 +325,7 @@ SOURCES += src/audiomixerboard.cpp \ src/serverlist.cpp \ src/serverlogging.cpp \ src/settings.cpp \ + src/signalhandler.cpp \ src/socket.cpp \ src/soundbase.cpp \ src/util.cpp \ diff --git a/src/client.cpp b/src/client.cpp index b4a60743..1259e3ee 100755 --- a/src/client.cpp +++ b/src/client.cpp @@ -1207,7 +1207,7 @@ fflush(pFileDelay); // update socket buffer size Channel.UpdateSocketBufferSize(); - Q_UNUSED ( iUnused ); + Q_UNUSED ( iUnused ) } int CClient::EstimatedOverallDelay ( const int iPingTimeMs ) diff --git a/src/recorder/jamrecorder.cpp b/src/recorder/jamrecorder.cpp index 1055efdc..2c817919 100755 --- a/src/recorder/jamrecorder.cpp +++ b/src/recorder/jamrecorder.cpp @@ -314,22 +314,31 @@ void CJamRecorder::Init( const CServer* server, throw std::runtime_error( (recordBaseDir.absolutePath() + " is a directory but cannot be written to").toStdString() ); } - QObject::connect((const QObject *)server, SIGNAL ( Stopped() ), - this, SLOT( OnEnd() ), - Qt::ConnectionType::QueuedConnection); + QObject::connect( (const QObject *)server, SIGNAL ( Stopped() ), + this, SLOT( OnEnd() ), + Qt::ConnectionType::QueuedConnection ); - QObject::connect((const QObject *)server, SIGNAL ( ClientDisconnected(int) ), - this, SLOT( OnDisconnected(int) ), - Qt::ConnectionType::QueuedConnection); + QObject::connect( (const QObject *)server, SIGNAL ( ClientDisconnected ( int ) ), + this, SLOT( OnDisconnected ( int ) ), + Qt::ConnectionType::QueuedConnection ); qRegisterMetaType>(); - QObject::connect((const QObject *)server, SIGNAL ( AudioFrame(const int, const QString, const CHostAddress, const int, const CVector) ), - this, SLOT( OnFrame(const int, const QString, const CHostAddress, const int, const CVector) ), - Qt::ConnectionType::QueuedConnection); + QObject::connect( (const QObject *)server, SIGNAL ( AudioFrame( const int, const QString, const CHostAddress, const int, const CVector ) ), + this, SLOT( OnFrame (const int, const QString, const CHostAddress, const int, const CVector ) ), + Qt::ConnectionType::QueuedConnection ); + + QObject::connect( QCoreApplication::instance(), + SIGNAL ( aboutToQuit() ), + this, SLOT( OnAboutToQuit() ) ); iServerFrameSizeSamples = _iServerFrameSizeSamples; + + thisThread = new QThread(); + moveToThread ( thisThread ); + thisThread->start(); } + /** * @brief CJamRecorder::OnStart Start up tasks when the first client connects */ @@ -337,7 +346,7 @@ void CJamRecorder::OnStart() { // Ensure any previous cleaning up has been done. OnEnd(); - currentSession = new CJamSession(recordBaseDir); + currentSession = new CJamSession( recordBaseDir ); isRecording = true; } @@ -346,7 +355,7 @@ void CJamRecorder::OnStart() { */ void CJamRecorder::OnEnd() { - if (isRecording) + if ( isRecording ) { isRecording = false; currentSession->End(); @@ -372,6 +381,13 @@ void CJamRecorder::OnEnd() } } +void CJamRecorder::OnAboutToQuit() +{ + OnEnd(); + + thisThread->exit(); +} + /** * @brief CJamRecorder::SessionDirToReaper Replica of CJamRecorder::OnEnd() but using the directory contents to construct the CReaperProject object * @param strSessionDirName @@ -409,11 +425,11 @@ void CJamRecorder::SessionDirToReaper(QString& strSessionDirName, int serverFram */ void CJamRecorder::OnDisconnected(int iChID) { - if (!isRecording) + if ( !isRecording ) { qWarning() << "CJamRecorder::OnDisconnected: channel" << iChID << "disconnected but not recording"; } - if (currentSession == nullptr) + if ( currentSession == nullptr ) { qWarning() << "CJamRecorder::OnDisconnected: channel" << iChID << "disconnected but no currentSession"; return; @@ -434,7 +450,7 @@ void CJamRecorder::OnDisconnected(int iChID) void CJamRecorder::OnFrame(const int iChID, const QString name, const CHostAddress address, const int numAudioChannels, const CVector data) { // Make sure we are ready - if (!isRecording) + if ( !isRecording ) { OnStart(); } diff --git a/src/recorder/jamrecorder.h b/src/recorder/jamrecorder.h index cc49705d..eb5a0581 100755 --- a/src/recorder/jamrecorder.h +++ b/src/recorder/jamrecorder.h @@ -132,13 +132,16 @@ private: QList jamClientConnections; }; -class CJamRecorder : public QThread +class CJamRecorder : public QObject { Q_OBJECT public: - CJamRecorder(const QString recordingDirName) : - recordBaseDir (recordingDirName), isRecording (false) {} + CJamRecorder ( const QString recordingDirName ) : + recordBaseDir ( recordingDirName ), + isRecording ( false ) + { + } void Init( const CServer* server, const int _iServerFrameSizeSamples ); @@ -155,16 +158,21 @@ public slots: */ void OnEnd(); + /** + * @brief Raised when application is stopping + */ + void OnAboutToQuit(); + /** * @brief Raised when an existing client leaves the server. * @param iChID channel number of client */ - void OnDisconnected(int iChID); + void OnDisconnected ( int iChID ); /** - * @brief Raised when a frame of data fis available to process + * @brief Raised when a frame of data is available to process */ - void OnFrame(const int iChID, const QString name, const CHostAddress address, const int numAudioChannels, const CVector data); + void OnFrame ( const int iChID, const QString name, const CHostAddress address, const int numAudioChannels, const CVector data ); private: QDir recordBaseDir; @@ -172,6 +180,8 @@ private: bool isRecording; CJamSession* currentSession; int iServerFrameSizeSamples; + + QThread* thisThread; }; } diff --git a/src/server.cpp b/src/server.cpp index 7ba26c9f..81612155 100755 --- a/src/server.cpp +++ b/src/server.cpp @@ -398,11 +398,10 @@ CServer::CServer ( const int iNewMaxNumChan, QString().number( static_cast ( iPortNumber ) ) ); } - // Enable jam recording (if requested) + // Enable jam recording (if requested) - kicks off the thread if ( bEnableRecording ) { JamRecorder.Init ( this, iServerFrameSizeSamples ); - JamRecorder.start(); } // enable all channels (for the server all channel must be enabled the @@ -466,6 +465,14 @@ CServer::CServer ( const int iNewMaxNumChan, SIGNAL ( SvrRegStatusChanged() ), this, SLOT ( OnSvrRegStatusChanged() ) ); + QObject::connect ( QCoreApplication::instance(), + SIGNAL ( aboutToQuit() ), + this, SLOT ( OnAboutToQuit() ) ); + + QObject::connect ( pSignalHandler, + SIGNAL ( ShutdownSignal ( int ) ), + this, SLOT ( OnShutdown ( int ) ) ); + #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) connectChannelSignalsToServerSlots(); @@ -908,6 +915,54 @@ void CServer::OnCLDisconnection ( CHostAddress InetAddr ) } } +void CServer::OnAboutToQuit() +{ + if ( IsRunning() ) + { + // Clean up + CleanShutdown(); + } + + // if server was registered at the central server, unregister on shutdown + if ( GetServerListEnabled() ) + { + UnregisterSlaveServer(); + } +} + +void CServer::OnShutdown ( int ) +{ + // This should trigger OnAboutToQuit + QCoreApplication::instance()->exit(); +} + + +void CServer::CleanShutdown() { + Mutex.lock(); + { + bool oldDAC = bDisconnectAllClients; + + // This is to prevent new connections + bDisconnectAllClients = true; + + // This is to disconnect all connected channels + for ( int i = 0; i < iMaxNumChannels; i++ ) + { + if ( vecChannels[i].IsConnected() ) + { + vecChannels[i].Disconnect(); + } + } + + // This should tell the jam recorder than the jam has ended... + Stop(); + + // Restore the value in case we are not exiting + bDisconnectAllClients = oldDAC; + } + Mutex.unlock(); +} + void CServer::Start() { // only start if not already running @@ -1273,7 +1328,7 @@ opus_custom_encoder_ctl ( CurOpusEncoder, Stop(); } - Q_UNUSED ( iUnused ); + Q_UNUSED ( iUnused ) } /// @brief Mix all audio data from all clients together. diff --git a/src/server.h b/src/server.h index d56a42c8..fc422c2c 100755 --- a/src/server.h +++ b/src/server.h @@ -36,6 +36,7 @@ #endif #include "global.h" #include "buffer.h" +#include "signalhandler.h" #include "socket.h" #include "channel.h" #include "util.h" @@ -257,6 +258,8 @@ protected: int GetNumberOfConnectedClients(); CVector CreateChannelList(); + CSignalHandler* pSignalHandler = CSignalHandler::getSingletonP(); + #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) virtual void CreateAndSendChanListForAllConChannels(); virtual void CreateAndSendChanListForThisChan ( const int iCurChanID ); @@ -303,6 +306,8 @@ protected: const CVector > vecvecsData, CVector& vecLevelsOut ); + void CleanShutdown(); + // do not use the vector class since CChannel does not have appropriate // copy constructor/operator CChannel vecChannels[MAX_NUM_CHANNELS]; @@ -455,6 +460,10 @@ public slots: void OnCLDisconnection ( CHostAddress InetAddr ); + void OnAboutToQuit(); + + void OnShutdown ( int ); + #if QT_VERSION < 0x50000 // MOC does not expand macros in Qt 4, so we cannot use QT_VERSION_CHECK(5, 0, 0) // CODE TAG: MAX_NUM_CHANNELS_TAG // make sure we have MAX_NUM_CHANNELS connections!!! diff --git a/src/serverdlg.cpp b/src/serverdlg.cpp index 7717e023..7df039ed 100755 --- a/src/serverdlg.cpp +++ b/src/serverdlg.cpp @@ -342,18 +342,6 @@ lvwClients->setMinimumHeight ( 140 ); Timer.start ( GUI_CONTRL_UPDATE_TIME ); } -void CServerDlg::closeEvent ( QCloseEvent* Event ) -{ - // if server was registered at the central server, unregister on shutdown - if ( pServer->GetServerListEnabled() ) - { - pServer->UnregisterSlaveServer(); - } - - // default implementation of this event handler routine - Event->accept(); -} - void CServerDlg::OnStartOnOSStartStateChanged ( int value ) { const bool bCurAutoStartMinState = ( value == Qt::Checked ); diff --git a/src/serverdlg.h b/src/serverdlg.h index febb44ef..da06e0c4 100755 --- a/src/serverdlg.h +++ b/src/serverdlg.h @@ -58,7 +58,6 @@ public: protected: virtual void changeEvent ( QEvent* pEvent ); - virtual void closeEvent ( QCloseEvent* Event ); void UpdateGUIDependencies(); void UpdateSystemTrayIcon ( const bool bIsActive ); diff --git a/src/signalhandler.cpp b/src/signalhandler.cpp new file mode 100755 index 00000000..648506e3 --- /dev/null +++ b/src/signalhandler.cpp @@ -0,0 +1,146 @@ +/******************************************************************************\ + * Copyright (c) 2020 + * + * Author(s): + * Peter L Jones + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ + +#include "signalhandler.h" + +class CSignalHandlerSingleton : public CSignalHandler { +public: + inline CSignalHandlerSingleton() : CSignalHandler() {} +}; +Q_GLOBAL_STATIC ( CSignalHandlerSingleton, singleton ) + +CSignalHandler::CSignalHandler() : pSignalBase ( CSignalBase::withSignalHandler ( this ) ) {} + +CSignalHandler::~CSignalHandler() = default; + +CSignalHandler* CSignalHandler::getSingletonP() { return singleton; } + +bool CSignalHandler::emitSignal ( int sigNum ) +{ + return QMetaObject::invokeMethod( singleton, "ShutdownSignal", Qt::QueuedConnection, Q_ARG( int, sigNum ) ); +} + +#ifndef _WIN32 +void CSignalHandler::OnSocketNotify( int socket ) +{ + int sigNum; + if ( ::read ( socket, &sigNum, sizeof ( int ) ) == sizeof ( int ) ) + { + emitSignal ( sigNum ); + } +} +#endif + +// ---------------------------------------------------------- + +CSignalBase::CSignalBase ( CSignalHandler* pSignalHandler ) : + pSignalHandler ( pSignalHandler ) +{ +} + +CSignalBase::~CSignalBase() = default; + +CSignalBase* CSignalBase::withSignalHandler ( CSignalHandler* pSignalHandler ) +{ +#ifdef _WIN32 + return new CSignalWin ( pSignalHandler ); +#else + return new CSignalUnix ( pSignalHandler ); +#endif +} + +#ifdef _WIN32 +CSignalWin::CSignalWin ( CSignalHandler* nPSignalHandler ) : + CSignalBase ( nPSignalHandler ), + lock ( QReadWriteLock::Recursive ) +{ + SetConsoleCtrlHandler ( signalHandler, true ); +} + +CSignalWin::~CSignalWin() { + SetConsoleCtrlHandler ( signalHandler, false ); +} + +QReadWriteLock* CSignalWin::getLock() const +{ + return &lock; +} + +BOOL WINAPI CSignalWin::signalHandler ( _In_ DWORD sigNum ) +{ + auto self = getSelf(); + QReadLocker lock ( self->getLock() ); + return self->pSignalHandler->emitSignal ( static_cast( sigNum ) ); +} + +#else +int CSignalUnix::socketPair[2]; + +CSignalUnix::CSignalUnix ( CSignalHandler* nPSignalHandler ) : + CSignalBase ( nPSignalHandler ) +{ + if ( ::socketpair ( AF_UNIX, SOCK_STREAM, 0, socketPair ) == 0 ) + { + socketNotifier = new QSocketNotifier ( socketPair[ 1 ], QSocketNotifier::Read ); + + QObject::connect ( socketNotifier, &QSocketNotifier::activated, nPSignalHandler, &CSignalHandler::OnSocketNotify ); + + socketNotifier->setEnabled ( true ); + + setSignalHandled ( SIGINT, true ); + setSignalHandled ( SIGTERM, true ); + } +} + +CSignalUnix::~CSignalUnix() { + setSignalHandled ( SIGINT, false ); + setSignalHandled ( SIGTERM, false ); +} + +QReadWriteLock* CSignalUnix::getLock() const { return nullptr; } + +bool CSignalUnix::setSignalHandled ( int sigNum, bool state ) +{ + struct sigaction sa; + sigemptyset ( &sa.sa_mask ); + + if ( state ) + { + sa.sa_handler = CSignalUnix::signalHandler; + sa.sa_flags |= SA_RESTART; + } + else + { + sa.sa_handler = SIG_DFL; + } + + return ::sigaction ( sigNum, &sa, nullptr ) == 0; +} + +void CSignalUnix::signalHandler ( int sigNum ) +{ + const auto res = ::write ( socketPair[ 0 ], &sigNum, sizeof ( int ) ); + Q_UNUSED ( res ); +} +#endif diff --git a/src/signalhandler.h b/src/signalhandler.h new file mode 100755 index 00000000..d1e571d7 --- /dev/null +++ b/src/signalhandler.h @@ -0,0 +1,140 @@ +/******************************************************************************\ + * Copyright (c) 2020 + * + * Author(s): + * Peter L Jones + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#endif + +class CSignalBase; + +class CSignalHandler : public QObject +{ + Q_OBJECT + + friend class CSignalBase; + friend class CSignalHandlerSingleton; + +public: + static CSignalHandler* getSingletonP(); + + bool emitSignal ( int ); + +#ifndef _WIN32 +public slots: + void OnSocketNotify ( int socket ); +#endif + +signals: + void ShutdownSignal ( int sigNum ); + +private: + QScopedPointer pSignalBase; + + explicit CSignalHandler(); + ~CSignalHandler() override; +}; + +// ---------------------------------------------------------- + +class CSignalBase +{ + Q_DISABLE_COPY ( CSignalBase ) + +public: + static CSignalBase* withSignalHandler ( CSignalHandler* ); + virtual ~CSignalBase(); + + virtual QReadWriteLock* getLock() const = 0; + + QSet sHandledSigNums; + +protected: + CSignalBase ( CSignalHandler* ); + + CSignalHandler* pSignalHandler; + + template + static T *getSelf() + { + return static_cast( CSignalHandler::getSingletonP()->pSignalBase.data() ); + } + +}; + +#ifdef _WIN32 + +class CSignalWin : public CSignalBase +{ +public: + CSignalWin ( CSignalHandler* ); + ~CSignalWin() override; + + virtual QReadWriteLock* getLock() const override; + +private: + mutable QReadWriteLock lock; + + static BOOL WINAPI signalHandler ( _In_ DWORD sigNum ); +}; + +#else + +class CSignalUnix : public CSignalBase +{ +public: + CSignalUnix ( CSignalHandler* ); + ~CSignalUnix() override; + + virtual QReadWriteLock* getLock() const override; + +private: + QSocketNotifier* socketNotifier = nullptr; + bool setSignalHandled ( int sigNum, bool state ); + + static int socketPair[2]; + static void signalHandler ( int sigNum ); +}; + +#endif From db357ae2bc30ef5c8938425bbe6b6bb648493532 Mon Sep 17 00:00:00 2001 From: Peter L Jones Date: Mon, 11 May 2020 20:12:16 +0100 Subject: [PATCH 2/2] Review comment fixes --- src/server.cpp | 36 +---- src/server.h | 6 +- src/signalhandler.cpp | 37 +++++ src/signalhandler.h | 317 +++++++++++++++++++++++------------------- 4 files changed, 219 insertions(+), 177 deletions(-) diff --git a/src/server.cpp b/src/server.cpp index 81612155..40e2e63b 100755 --- a/src/server.cpp +++ b/src/server.cpp @@ -251,7 +251,8 @@ CServer::CServer ( const int iNewMaxNumChan, bAutoRunMinimized ( false ), strWelcomeMessage ( strNewWelcomeMessage ), eLicenceType ( eNLicenceType ), - bDisconnectAllClients ( bNDisconnectAllClients ) + bDisconnectAllClients ( bNDisconnectAllClients ), + pSignalHandler ( CSignalHandler::getSingletonP() ) { int iOpusError; int i; @@ -917,11 +918,7 @@ void CServer::OnCLDisconnection ( CHostAddress InetAddr ) void CServer::OnAboutToQuit() { - if ( IsRunning() ) - { - // Clean up - CleanShutdown(); - } + Stop(); // if server was registered at the central server, unregister on shutdown if ( GetServerListEnabled() ) @@ -936,33 +933,6 @@ void CServer::OnShutdown ( int ) QCoreApplication::instance()->exit(); } - -void CServer::CleanShutdown() { - Mutex.lock(); - { - bool oldDAC = bDisconnectAllClients; - - // This is to prevent new connections - bDisconnectAllClients = true; - - // This is to disconnect all connected channels - for ( int i = 0; i < iMaxNumChannels; i++ ) - { - if ( vecChannels[i].IsConnected() ) - { - vecChannels[i].Disconnect(); - } - } - - // This should tell the jam recorder than the jam has ended... - Stop(); - - // Restore the value in case we are not exiting - bDisconnectAllClients = oldDAC; - } - Mutex.unlock(); -} - void CServer::Start() { // only start if not already running diff --git a/src/server.h b/src/server.h index fc422c2c..d68cac38 100755 --- a/src/server.h +++ b/src/server.h @@ -258,8 +258,6 @@ protected: int GetNumberOfConnectedClients(); CVector CreateChannelList(); - CSignalHandler* pSignalHandler = CSignalHandler::getSingletonP(); - #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) virtual void CreateAndSendChanListForAllConChannels(); virtual void CreateAndSendChanListForThisChan ( const int iCurChanID ); @@ -306,8 +304,6 @@ protected: const CVector > vecvecsData, CVector& vecLevelsOut ); - void CleanShutdown(); - // do not use the vector class since CChannel does not have appropriate // copy constructor/operator CChannel vecChannels[MAX_NUM_CHANNELS]; @@ -375,6 +371,8 @@ protected: ELicenceType eLicenceType; bool bDisconnectAllClients; + CSignalHandler* pSignalHandler; + signals: void Started(); void Stopped(); diff --git a/src/signalhandler.cpp b/src/signalhandler.cpp index 648506e3..4c37d018 100755 --- a/src/signalhandler.cpp +++ b/src/signalhandler.cpp @@ -20,6 +20,43 @@ * this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + ****************************************************************************** + * + * This code contains some ideas derived from QCtrlSignals + * https://github.com/Skycoder42/QCtrlSignals.git + * - mostly the singleton and emitSignal code, plus some of the structure + * - virtually everything else is common knowledge across SourceForge answers + * + * BSD 3-Clause License + * + * Copyright (c) 2016, Felix Barz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * \******************************************************************************/ #include "signalhandler.h" diff --git a/src/signalhandler.h b/src/signalhandler.h index d1e571d7..1263ee6a 100755 --- a/src/signalhandler.h +++ b/src/signalhandler.h @@ -1,140 +1,177 @@ -/******************************************************************************\ - * Copyright (c) 2020 - * - * Author(s): - * Peter L Jones - * - ****************************************************************************** - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -\******************************************************************************/ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#include -#include -#include -#include -#else -#include -#include -#include -#include -#include -#include -#include -#endif - -class CSignalBase; - -class CSignalHandler : public QObject -{ - Q_OBJECT - - friend class CSignalBase; - friend class CSignalHandlerSingleton; - -public: - static CSignalHandler* getSingletonP(); - - bool emitSignal ( int ); - -#ifndef _WIN32 -public slots: - void OnSocketNotify ( int socket ); -#endif - -signals: - void ShutdownSignal ( int sigNum ); - -private: - QScopedPointer pSignalBase; - - explicit CSignalHandler(); - ~CSignalHandler() override; -}; - -// ---------------------------------------------------------- - -class CSignalBase -{ - Q_DISABLE_COPY ( CSignalBase ) - -public: - static CSignalBase* withSignalHandler ( CSignalHandler* ); - virtual ~CSignalBase(); - - virtual QReadWriteLock* getLock() const = 0; - - QSet sHandledSigNums; - -protected: - CSignalBase ( CSignalHandler* ); - - CSignalHandler* pSignalHandler; - - template - static T *getSelf() - { - return static_cast( CSignalHandler::getSingletonP()->pSignalBase.data() ); - } - -}; - -#ifdef _WIN32 - -class CSignalWin : public CSignalBase -{ -public: - CSignalWin ( CSignalHandler* ); - ~CSignalWin() override; - - virtual QReadWriteLock* getLock() const override; - -private: - mutable QReadWriteLock lock; - - static BOOL WINAPI signalHandler ( _In_ DWORD sigNum ); -}; - -#else - -class CSignalUnix : public CSignalBase -{ -public: - CSignalUnix ( CSignalHandler* ); - ~CSignalUnix() override; - - virtual QReadWriteLock* getLock() const override; - -private: - QSocketNotifier* socketNotifier = nullptr; - bool setSignalHandled ( int sigNum, bool state ); - - static int socketPair[2]; - static void signalHandler ( int sigNum ); -}; - -#endif +/******************************************************************************\ + * Copyright (c) 2020 + * + * Author(s): + * Peter L Jones + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************** + * + * This code contains some ideas derived from QCtrlSignals + * https://github.com/Skycoder42/QCtrlSignals.git + * - mostly the singleton and emitSignal code, plus some of the structure + * - virtually everything else is common knowledge across SourceForge answers + * + * BSD 3-Clause License + * + * Copyright (c) 2016, Felix Barz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * +\******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#endif + +class CSignalBase; + +class CSignalHandler : public QObject +{ + Q_OBJECT + + friend class CSignalBase; + friend class CSignalHandlerSingleton; + +public: + static CSignalHandler* getSingletonP(); + + bool emitSignal ( int ); + +#ifndef _WIN32 +public slots: + void OnSocketNotify ( int socket ); +#endif + +signals: + void ShutdownSignal ( int sigNum ); + +private: + QScopedPointer pSignalBase; + + explicit CSignalHandler(); + ~CSignalHandler() override; +}; + +// ---------------------------------------------------------- + +class CSignalBase +{ + Q_DISABLE_COPY ( CSignalBase ) + +public: + static CSignalBase* withSignalHandler ( CSignalHandler* ); + virtual ~CSignalBase(); + + virtual QReadWriteLock* getLock() const = 0; + + QSet sHandledSigNums; + +protected: + CSignalBase ( CSignalHandler* ); + + CSignalHandler* pSignalHandler; + + template + static T *getSelf() + { + return static_cast( CSignalHandler::getSingletonP()->pSignalBase.data() ); + } + +}; + +#ifdef _WIN32 + +class CSignalWin : public CSignalBase +{ +public: + CSignalWin ( CSignalHandler* ); + ~CSignalWin() override; + + virtual QReadWriteLock* getLock() const override; + +private: + mutable QReadWriteLock lock; + + static BOOL WINAPI signalHandler ( _In_ DWORD sigNum ); +}; + +#else + +class CSignalUnix : public CSignalBase +{ +public: + CSignalUnix ( CSignalHandler* ); + ~CSignalUnix() override; + + virtual QReadWriteLock* getLock() const override; + +private: + QSocketNotifier* socketNotifier = nullptr; + bool setSignalHandled ( int sigNum, bool state ); + + static int socketPair[2]; + static void signalHandler ( int sigNum ); +}; + +#endif