From: Vadim Zeitlin Date: Wed, 3 Jul 2013 00:31:03 +0000 (+0000) Subject: Make Unix wxAppConsole signal handling more flexible. X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/d5ab427f23dad393d9d867afeb1af3892ce00f3b Make Unix wxAppConsole signal handling more flexible. Instead of hardcoding the call to WakeUpIdle() in the signal handler itself, just wake up the event loop when we catch a signal. This will still result in WakeUpIdle() being called, before the next event loop iteration, but it will also allow us to do other things on wakeup from signal as it will be done in the upcoming wxExecute() changes to support wxEXEC_NOEVENTS in console applications. See #10258. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74347 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/include/wx/unix/app.h b/include/wx/unix/app.h index ca3f3744c0..812783bcef 100644 --- a/include/wx/unix/app.h +++ b/include/wx/unix/app.h @@ -11,10 +11,17 @@ //Ensure that sigset_t is being defined #include +class wxFDIODispatcher; +class wxFDIOHandler; +class wxWakeUpPipe; + // wxApp subclass implementing event processing for console applications class WXDLLIMPEXP_BASE wxAppConsole : public wxAppConsoleBase { public: + wxAppConsole(); + virtual ~wxAppConsole(); + // override base class initialization virtual bool Initialize(int& argc, wxChar** argv); @@ -38,6 +45,14 @@ public: // handlers for them void CheckSignal(); + // Register the signal wake up pipe with the given dispatcher. + // + // This is not used anywhere yet but will be soon. + // + // The pointer to the handler used for processing events on this descriptor + // is returned so that it can be deleted when we no longer needed it. + wxFDIOHandler* RegisterSignalWakeUpPipe(wxFDIODispatcher& dispatcher); + private: // signal handler set up by SetSignalHandler() for all signals we handle, // it just adds the signal to m_signalsCaught -- the real processing is @@ -52,4 +67,8 @@ private: // the signal handlers WX_DECLARE_HASH_MAP(int, SignalHandler, wxIntegerHash, wxIntegerEqual, SignalHandlerHash); SignalHandlerHash m_signalHandlerHash; + + // pipe used for wake up signal handling: if a signal arrives while we're + // blocking for input, writing to this pipe triggers a call to our CheckSignal() + wxWakeUpPipe *m_signalWakeUpPipe; }; diff --git a/src/unix/appunix.cpp b/src/unix/appunix.cpp index 5c59dec71c..f0733e2ada 100644 --- a/src/unix/appunix.cpp +++ b/src/unix/appunix.cpp @@ -20,6 +20,10 @@ #endif #include "wx/evtloop.h" +#include "wx/scopedptr.h" +#include "wx/unix/private/wakeuppipe.h" +#include "wx/private/fdiodispatcher.h" +#include "wx/private/fdioeventloopsourcehandler.h" #include #include @@ -29,6 +33,58 @@ #define SA_RESTART 0 #endif +// ---------------------------------------------------------------------------- +// Helper class calling CheckSignal() on wake up +// ---------------------------------------------------------------------------- + +namespace +{ + +class SignalsWakeUpPipe : public wxWakeUpPipe +{ +public: + // Ctor automatically registers this pipe with the event loop. + SignalsWakeUpPipe() + { + m_source = wxEventLoopBase::AddSourceForFD + ( + GetReadFd(), + this, + wxEVENT_SOURCE_INPUT + ); + } + + virtual void OnReadWaiting() + { + // The base class wxWakeUpPipe::OnReadWaiting() needs to be called in order + // to read the data out of the wake up pipe and clear it for next time. + wxWakeUpPipe::OnReadWaiting(); + + if ( wxTheApp ) + wxTheApp->CheckSignal(); + } + + virtual ~SignalsWakeUpPipe() + { + delete m_source; + } + +private: + wxEventLoopSource* m_source; +}; + +} // anonymous namespace + +wxAppConsole::wxAppConsole() +{ + m_signalWakeUpPipe = NULL; +} + +wxAppConsole::~wxAppConsole() +{ + delete m_signalWakeUpPipe; +} + // use unusual names for arg[cv] to avoid clashes with wxApp members with the // same names bool wxAppConsole::Initialize(int& argc_, wxChar** argv_) @@ -41,14 +97,23 @@ bool wxAppConsole::Initialize(int& argc_, wxChar** argv_) return true; } +// The actual signal handler. It does as little as possible (because very few +// things are safe to do from inside a signal handler) and just ensures that +// CheckSignal() will be called later from SignalsWakeUpPipe::OnReadWaiting(). void wxAppConsole::HandleSignal(int signal) { wxAppConsole * const app = wxTheApp; if ( !app ) return; + // Register the signal that is caught. sigaddset(&(app->m_signalsCaught), signal); - app->WakeUpIdle(); + + // Wake up the application for handling the signal. + // + // Notice that we must have a valid wake up pipe here as we only install + // our signal handlers after allocating it. + app->m_signalWakeUpPipe->WakeUpNoLock(); } void wxAppConsole::CheckSignal() @@ -66,6 +131,27 @@ void wxAppConsole::CheckSignal() } } +wxFDIOHandler* wxAppConsole::RegisterSignalWakeUpPipe(wxFDIODispatcher& dispatcher) +{ + wxCHECK_MSG( m_signalWakeUpPipe, false, "Should be allocated" ); + + // we need a bridge to wxFDIODispatcher + // + // TODO: refactor the code so that only wxEventLoopSourceHandler is used + wxScopedPtr + fdioHandler(new wxFDIOEventLoopSourceHandler(m_signalWakeUpPipe)); + + if ( !dispatcher.RegisterFD + ( + m_signalWakeUpPipe->GetReadFd(), + fdioHandler.get(), + wxFDIO_INPUT + ) ) + return NULL; + + return fdioHandler.release(); +} + // the type of the signal handlers we use is "void(*)(int)" while the real // signal handlers are extern "C" and so have incompatible type and at least // Sun CC warns about it, so use explicit casts to suppress these warnings as @@ -80,6 +166,13 @@ bool wxAppConsole::SetSignalHandler(int signal, SignalHandler handler) const bool install = (SignalHandler_t)handler != SIG_DFL && (SignalHandler_t)handler != SIG_IGN; + if ( !m_signalWakeUpPipe ) + { + // Create the pipe that the signal handler will use to cause the event + // loop to call wxAppConsole::CheckSignal(). + m_signalWakeUpPipe = new SignalsWakeUpPipe(); + } + struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = (SignalHandler_t)&wxAppConsole::HandleSignal;