PosixSignalWatcher

This commit is contained in:
Andrei Astafev 2020-06-27 10:04:56 +03:00
parent 44cd25464f
commit ca83f98484
7 changed files with 378 additions and 6 deletions

View File

@ -0,0 +1,83 @@
# Название основной цели в текущем каталоге
set(TRGT example-qt-posix-signal-watcher)
# Список файлов исходных текстов
set(TRGT_cpp ${CMAKE_CURRENT_SOURCE_DIR}/posix_signal_watcher.cpp)
if(MYXLIB_BUILD_EXAMPLES)
# Путь поиска библиотек внутри проекта
link_directories(${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
# Цель для создания исполняемого файла
add_executable(${TRGT} ${TRGT_cpp})
common_target_properties(${TRGT})
# Создание цели для проверки утилитой clang-tidy
add_clang_tidy_check(${TRGT} ${TRGT_cpp})
# Создание цели для проверки утилитой clang-analyze
add_clang_analyze_check(${TRGT} ${TRGT_cpp})
# Создание цели для проверки утилитой clazy
add_clazy_check(${TRGT} ${TRGT_cpp})
# Создание цели для проверки утилитой pvs-studio
add_pvs_check(${TRGT})
# Создание цели для автоматического форматирования кода
add_format_sources(${TRGT} ${TRGT_cpp})
# Qt5
target_include_directories(${TRGT} PRIVATE ${CMAKE_SOURCE_DIR}/src)
target_include_directories(${TRGT} SYSTEM PUBLIC ${Qt5Core_INCLUDE_DIRS})
target_include_directories(${TRGT} SYSTEM PRIVATE ${CMAKE_SOURCE_DIR}/src)
add_dependencies(${TRGT} base qt)
target_link_libraries(${TRGT} base_static qt_static)
target_link_libraries(${TRGT} Qt5::Core)
target_link_libraries(${TRGT} Threads::Threads)
# Имя выходного файла для цели
set_target_properties(${TRGT} PROPERTIES OUTPUT_NAME qt-posix-signal-watcher)
add_sanitizers(${TRGT})
cotire(${TRGT})
add_dependencies(${TRGT} create_auxilary_symlinks)
# Правила для установки
install(TARGETS ${TRGT} COMPONENT examples RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
if(MYXLIB_BUILD_EXAMPLES_HO)
set(TRGT_moc_hpp
${CMAKE_SOURCE_DIR}/src/myx/qt/posix_signal_watcher.hpp
${CMAKE_SOURCE_DIR}/src/myx/qt/posix_signal_watcher_p.hpp)
qt5_wrap_cpp(TRGT_moc_cpp ${TRGT_moc_hpp})
# Цель для создания исполняемого файла
add_executable(${TRGT}-ho ${TRGT_cpp} ${TRGT_moc_cpp})
common_target_properties(${TRGT}-ho)
target_include_directories(${TRGT}-ho PRIVATE ${CMAKE_SOURCE_DIR}/src)
target_include_directories(${TRGT}-ho SYSTEM PUBLIC ${Qt5Core_INCLUDE_DIRS})
add_dependencies(${TRGT}-ho base-header-only qt-header-only)
target_link_libraries(${TRGT}-ho Qt5::Core)
target_link_libraries(${TRGT}-ho Threads::Threads)
# Имя выходного файла для цели
set_target_properties(${TRGT}-ho PROPERTIES OUTPUT_NAME qt-posix-signal-watcher-ho)
add_sanitizers(${TRGT}-ho)
cotire(${TRGT}-ho)
add_dependencies(${TRGT}-ho create_auxilary_symlinks)
# Правила для установки
install(TARGETS ${TRGT}-ho COMPONENT examples RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()

View File

@ -0,0 +1,21 @@
#include <myx/qt/posix_signal_watcher.hpp>
#include <QCoreApplication>
#include <QDebug>
namespace MQ = myx::qt;
int main( int argc, char* argv[] )
{
QCoreApplication app( argc, argv );
qDebug() << "Hello from process" << QCoreApplication::applicationPid();
MQ::PosixSignalWatcher sigwatch;
sigwatch.watchForSignal( SIGINT );
sigwatch.watchForSignal( SIGTERM );
QObject::connect( &sigwatch, &MQ::PosixSignalWatcher::posixSignal, &app, &QCoreApplication::quit );
int exitcode = app.exec();
qDebug() << "Goodbye";
return( exitcode );
}

View File

@ -1 +1,2 @@
add_subdirectory(01_translators)
add_subdirectory(02_posix-signal-watcher)

View File

@ -4,23 +4,36 @@ set(TRGT qt)
# cmake-format: off
# Список файлов исходных текстов
set(TRGT_cpp
${CMAKE_CURRENT_SOURCE_DIR}/translators.cpp)
${CMAKE_CURRENT_SOURCE_DIR}/posix_signal_watcher.cpp
${CMAKE_CURRENT_SOURCE_DIR}/translators.cpp
)
set(TRGT_moc_hpp
${CMAKE_CURRENT_SOURCE_DIR}/posix_signal_watcher.hpp
)
set(TRGT_moc_private_hpp
${CMAKE_CURRENT_SOURCE_DIR}/posix_signal_watcher_p.hpp
)
# Список заголовочных файлов (используется для установки)
set(TRGT_hpp
${CMAKE_CURRENT_SOURCE_DIR}/backports.hpp
${CMAKE_CURRENT_SOURCE_DIR}/translators.hpp)
${CMAKE_CURRENT_SOURCE_DIR}/translators.hpp
)
set(TRGT_headers ${TRGT_hpp})
set(TRGT_headers ${TRGT_moc_hpp} ${TRGT_hpp})
# cmake-format: on
qt5_wrap_cpp(TRGT_moc_cpp ${TRGT_moc_private_hpp} ${TRGT_moc_hpp})
add_library(${TRGT}-header-only INTERFACE)
target_include_directories(
${TRGT}-header-only SYSTEM INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
if(MYXLIB_BUILD_LIBRARIES)
add_common_library(${TRGT} OUTPUT_NAME myx-${TRGT} SOURCES ${TRGT_cpp} ${TRGT_headers})
add_common_library(${TRGT} OUTPUT_NAME myx-${TRGT} SOURCES ${TRGT_cpp} ${TRGT_moc_cpp} ${TRGT_moc_private_hpp} ${TRGT_headers})
common_target_properties(${TRGT})
# Создание цели для проверки утилитой clang-tidy
@ -36,11 +49,12 @@ if(MYXLIB_BUILD_LIBRARIES)
add_pvs_check(${TRGT})
# Создание цели для автоматического форматирования кода
add_format_sources(${TRGT} ${TRGT_cpp} ${TRGT_headers})
add_format_sources(${TRGT} ${TRGT_cpp} ${TRGT_headers} ${TRGT_moc_private_hpp})
target_compile_definitions(${TRGT} PUBLIC MYXLIB_BUILD_LIBRARIES)
target_include_directories(${TRGT} SYSTEM PUBLIC ${Qt5Core_INCLUDE_DIRS})
target_include_directories(${TRGT} SYSTEM PRIVATE ${CMAKE_SOURCE_DIR}/src)
target_include_directories(${TRGT} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
cotire(${TRGT})
install(TARGETS ${TRGT}_static COMPONENT libs-dev ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})

View File

@ -0,0 +1,136 @@
#ifndef MYX_QT_POSIX_SIGNAL_WATCHER_CPP_
#define MYX_QT_POSIX_SIGNAL_WATCHER_CPP_
#include <myx/base/config.hpp>
#ifndef MYXLIB_HEADER_ONLY
#include <myx/qt/posix_signal_watcher_p.hpp>
#include <myx/qt/posix_signal_watcher.hpp>
#else
#pragma once
#endif
namespace myx {
namespace qt {
int PosixSignalWatcherPrivate::m_sockpair[2] = {0, 0};
PosixSignalWatcherPrivate::~PosixSignalWatcherPrivate() = default;
PosixSignalWatcherPrivate::PosixSignalWatcherPrivate( PosixSignalWatcher* q ) :
q_ptr( q )
{
#if MYX_QT_HAS_POSIX_SIGNALS
// Create socket pair
if ( ::socketpair( AF_UNIX, SOCK_STREAM, 0, m_sockpair ) )
{
qDebug() << "PosixSignalWatcher: socketpair: " << ::strerror( errno );
return;
}
#endif
// Create a notifier for the read end of the pair
m_notifier.reset( new QSocketNotifier( m_sockpair[1], QSocketNotifier::Read ) );
// Called when the signal handler has written to the socket pair.
// Emits the Posix signal as a Qt signal.
connect( m_notifier.get(), &QSocketNotifier::activated, q, [this]( int sockfd ) {
Q_Q( PosixSignalWatcher );
int signal = 0;
(void)::read( sockfd, &signal, sizeof( signal ) );
qDebug() << "Caught signal: " << ::strsignal( signal );
Q_EMIT q->posixSignal( signal );
} );
m_notifier->setEnabled( true );
}
/*!
* Registers a handler for the given Posix \a signal. The handler will write to
* a socket pair, the other end of which is connected to a QSocketNotifier.
* This provides a way to break out of the asynchronous context from which the
* signal handler is called and back into the Qt event loop.
*/
void PosixSignalWatcherPrivate::watchForSignal( int signal )
{
if ( m_watchedSignals.contains( signal ) )
{
qDebug() << "Already watching for signal " << signal;
return;
}
#if MYX_QT_HAS_POSIX_SIGNALS
// Register a sigaction which will write to the socket pair
struct sigaction sigact;
sigact.sa_handler = PosixSignalWatcherPrivate::signalHandler;
sigact.sa_flags = 0;
sigemptyset( &sigact.sa_mask );
sigact.sa_flags |= SA_RESTART;
if ( ::sigaction( signal, &sigact, nullptr ) )
{
qDebug() << "PosixSignalWatcher: sigaction: " << ::strerror( errno );
return;
}
#endif
m_watchedSignals.append( signal );
} // PosixSignalWatcherPrivate::watchForSignal
/*!
* Called when a Posix \a signal is received. Write to the socket to wake up the
* QSocketNotifier.
*/
MYXLIB_INLINE void PosixSignalWatcherPrivate::signalHandler( int signal )
{
(void)::write( m_sockpair[0], &signal, sizeof( signal ) );
}
/*!
* Create a new PosixSignalWatcher as a child of the given \a parent.
*/
MYXLIB_INLINE PosixSignalWatcher::PosixSignalWatcher( QObject* parent ) :
QObject( parent ),
d_ptr ( new PosixSignalWatcherPrivate( this ) )
{
}
MYXLIB_INLINE PosixSignalWatcher::~PosixSignalWatcher() = default;
/*!
* Register a signal handler for the given \a signal.
*
* After calling this method you can \c connect() to the POSIXSignal() Qt signal
* to be notified when the Posix signal is received.
*/
MYXLIB_INLINE void PosixSignalWatcher::watchForSignal( int signal )
{
Q_D( PosixSignalWatcher );
d->watchForSignal( signal );
}
/*!
* \fn void PosixSignalWatcher::posixSignal(int signal)
* Emitted when the given Posix \a signal is received.
*
* watchForSignal() must be called for each Posix signal that you want to receive
* via the POSIXSignal() Qt signal. If a watcher is watching multiple signals,
* POSIXSignal() will be emitted whenever *any* of the watched Posix signals are
* received, and the \a signal argument can be inspected to find out which one
* was actually received.
*/
} // namespace qt
} // namespace myx
#endif // ifndef MYX_QT_POSIX_SIGNAL_WATCHER_CPP_

View File

@ -0,0 +1,55 @@
#ifndef MYX_QT_POSIX_SIGNAL_WATCHER_HPP_
#define MYX_QT_POSIX_SIGNAL_WATCHER_HPP_
#pragma once
#include <myx/base/config.hpp>
#include <QObject>
#include <csignal>
namespace myx {
namespace qt {
class PosixSignalWatcherPrivate;
#if defined( Q_OS_WIN )
const int SIGINT = 2;
const int SIGTERM = 15;
#endif
/*!
* \brief The PosixSignalWatcher class converts Posix signals to Qt signals.
*
* To watch for a given signal, e.g. \c SIGINT, call \c watchForSignal(SIGINT)
* and \c connect() your handler to posixSignal().
*/
class PosixSignalWatcher : public QObject
{
Q_OBJECT
public:
explicit PosixSignalWatcher( QObject* parent = nullptr );
~PosixSignalWatcher();
void watchForSignal( int signal );
Q_SIGNAL void posixSignal( int signal );
private:
PosixSignalWatcherPrivate* const d_ptr = nullptr;
Q_DECLARE_PRIVATE( PosixSignalWatcher )
}; // class PosixSignalWatcher
} // namespace qt
} // namespace myx
#ifdef MYXLIB_HEADER_ONLY
#include "posix_signal_watcher_p.hpp"
#include "posix_signal_watcher.cpp"
#endif
#endif // ifndef MYX_QT_POSIX_SIGNAL_WATCHER_HPP_

View File

@ -0,0 +1,62 @@
#ifndef MYX_QT_POSIX_SIGNAL_WATCHER_P_HPP_
#define MYX_QT_POSIX_SIGNAL_WATCHER_P_HPP_
#include <myx/base/config.hpp>
#include <myx/qt/posix_signal_watcher.hpp>
#include <QObject>
#include <QMap>
#include <QSocketNotifier>
#include <QtDebug>
#include <memory>
#ifdef Q_OS_WIN
#define MYX_QT_HAS_POSIX_SIGNALS 0
#else
#define MYX_QT_HAS_POSIX_SIGNALS 1
#endif
#if MYX_QT_HAS_POSIX_SIGNALS
#include <csignal>
#include <sys/socket.h>
#include <unistd.h>
#include <cerrno>
#endif
namespace myx {
namespace qt {
/*!
* \brief The PosixSignalWatcherPrivate class implements the back-end signal
* handling for the PosixSignalWatcher.
*
* \see http://qt-project.org/doc/qt-5.0/qtdoc/POSIX-signals.html
*/
class PosixSignalWatcherPrivate : public QObject
{
Q_OBJECT
public:
PosixSignalWatcherPrivate( PosixSignalWatcher* q );
~PosixSignalWatcherPrivate();
void watchForSignal( int signal );
static void signalHandler( int signal );
private:
PosixSignalWatcher* const q_ptr = nullptr;
Q_DECLARE_PUBLIC( PosixSignalWatcher )
static int m_sockpair[2];
std::unique_ptr< QSocketNotifier > m_notifier;
QList< int > m_watchedSignals;
}; // class PosixSignalWatcherPrivate
} // namespace qt
} // namespace myx
#endif // ifndef MYX_QT_POSIX_SIGNAL_WATCHER_P_HPP_