myxlib/src/myx/qt/posix_signal_watcher.cpp

137 lines
3.6 KiB
C++
Raw Normal View History

2020-06-27 07:04:56 +00:00
#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_