X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/a1873279648582e6af46f9103460da50bf5e03b9..64ea838d8f4d1853b7d850db93ee565e901d099a:/src/unix/evtloopunix.cpp diff --git a/src/unix/evtloopunix.cpp b/src/unix/evtloopunix.cpp index 39afb42db0..a4f67ecad6 100644 --- a/src/unix/evtloopunix.cpp +++ b/src/unix/evtloopunix.cpp @@ -21,30 +21,75 @@ #if wxUSE_CONSOLE_EVENTLOOP +#include "wx/evtloop.h" + #ifndef WX_PRECOMP #include "wx/app.h" #include "wx/log.h" #endif #include -#include "wx/evtloop.h" +#include "wx/apptrait.h" +#include "wx/scopedptr.h" #include "wx/thread.h" #include "wx/module.h" +#include "wx/unix/pipe.h" #include "wx/unix/private/timer.h" #include "wx/unix/private/epolldispatcher.h" #include "wx/private/selectdispatcher.h" -#define TRACE_EVENTS _T("events") +#if wxUSE_EVENTLOOP_SOURCE + #include "wx/evtloopsrc.h" +#endif // wxUSE_EVENTLOOP_SOURCE + +#define TRACE_EVENTS wxT("events") // =========================================================================== // wxEventLoop::PipeIOHandler implementation // =========================================================================== +namespace wxPrivate +{ + +// pipe used for wake up messages: when a child thread wants to wake up +// the event loop in the main thread it writes to this pipe +class PipeIOHandler : public wxFDIOHandler +{ +public: + // default ctor does nothing, call Create() to really initialize the + // object + PipeIOHandler() : m_pipeIsEmpty(true) { } + + bool Create(); + + // this method can be, and normally is, called from another thread + void WakeUp(); + + int GetReadFd() { return m_pipe[wxPipe::Read]; } + + // implement wxFDIOHandler pure virtual methods + virtual void OnReadWaiting(); + virtual void OnWriteWaiting() { } + virtual void OnExceptionWaiting() { } + +private: + wxPipe m_pipe; + + // Protects access to m_pipeIsEmpty. + wxCriticalSection m_pipeLock; + + // This flag is set to true after writing to the pipe and reset to false + // after reading from it in the main thread. Having it allows us to avoid + // overflowing the pipe with too many writes if the main thread can't keep + // up with reading from it. + bool m_pipeIsEmpty; +}; + // ---------------------------------------------------------------------------- // initialization // ---------------------------------------------------------------------------- -bool wxConsoleEventLoop::PipeIOHandler::Create() +bool PipeIOHandler::Create() { if ( !m_pipe.Create() ) { @@ -52,17 +97,15 @@ bool wxConsoleEventLoop::PipeIOHandler::Create() return false; } - const int fdRead = GetReadFd(); - int flags = fcntl(fdRead, F_GETFL, 0); - if ( flags == -1 || fcntl(fdRead, F_SETFL, flags | O_NONBLOCK) == -1 ) + if ( !m_pipe.MakeNonBlocking(wxPipe::Read) ) { wxLogSysError(_("Failed to switch wake up pipe to non-blocking mode")); return false; } wxLogTrace(TRACE_EVENTS, wxT("Wake up pipe (%d, %d) created"), - fdRead, m_pipe[wxPipe::Write]); + m_pipe[wxPipe::Read], m_pipe[wxPipe::Write]); return true; } @@ -71,44 +114,75 @@ bool wxConsoleEventLoop::PipeIOHandler::Create() // wakeup handling // ---------------------------------------------------------------------------- -void wxConsoleEventLoop::PipeIOHandler::WakeUp() +void PipeIOHandler::WakeUp() { + wxCriticalSectionLocker lock(m_pipeLock); + + // No need to do anything if the pipe already contains something. + if ( !m_pipeIsEmpty ) + return; + if ( write(m_pipe[wxPipe::Write], "s", 1) != 1 ) { // don't use wxLog here, we can be in another thread and this could // result in dead locks perror("write(wake up pipe)"); } + else + { + // We just wrote to it, so it's not empty any more. + m_pipeIsEmpty = false; + } } -void wxConsoleEventLoop::PipeIOHandler::OnReadWaiting() +void PipeIOHandler::OnReadWaiting() { - // got wakeup from child thread: read all data available in pipe just to - // make it empty (evevn though we write one byte at a time from WakeUp(), - // it could have been called several times) + // got wakeup from child thread, remove the data that provoked it from the + // pipe + + wxCriticalSectionLocker lock(m_pipeLock); + char buf[4]; for ( ;; ) { const int size = read(GetReadFd(), buf, WXSIZEOF(buf)); - if ( size == 0 || (size == -1 && errno == EAGAIN) ) + if ( size > 0 ) { - // nothing left in the pipe (EAGAIN is expected for an FD with - // O_NONBLOCK) + wxASSERT_MSG( size == 1, "Too many writes to wake-up pipe?" ); + break; } - if ( size == -1 ) + if ( size == 0 || (size == -1 && errno == EAGAIN) ) { - wxLogSysError(_("Failed to read from wake-up pipe")); - + // No data available, not an error (but still surprising, + // spurious wakeup?) break; } + + if ( errno == EINTR ) + { + // We were interrupted, try again. + continue; + } + + wxLogSysError(_("Failed to read from wake-up pipe")); + + return; } - wxTheApp->ProcessPendingEvents(); + // The pipe is empty now, so future calls to WakeUp() would need to write + // to it again. + m_pipeIsEmpty = true; + + // writing to the wake up pipe will make wxConsoleEventLoop return from + // wxFDIODispatcher::Dispatch() it might be currently blocking in, nothing + // else needs to be done } +} // namespace wxPrivate + // =========================================================================== // wxEventLoop implementation // =========================================================================== @@ -119,76 +193,172 @@ void wxConsoleEventLoop::PipeIOHandler::OnReadWaiting() wxConsoleEventLoop::wxConsoleEventLoop() { - if ( !m_wakeupPipe.Create() ) + m_wakeupPipe = new wxPrivate::PipeIOHandler(); + if ( !m_wakeupPipe->Create() ) { + wxDELETE(m_wakeupPipe); m_dispatcher = NULL; return; } -#ifdef wxUSE_EPOLL_DISPATCHER - m_dispatcher = wxEpollDispatcher::Get(); + m_dispatcher = wxFDIODispatcher::Get(); if ( !m_dispatcher ) -#endif // wxUSE_EPOLL_DISPATCHER -#if wxUSE_SELECT_DISPATCHER - m_dispatcher = wxSelectDispatcher::Get(); -#endif // wxUSE_WCHAR_T - - wxCHECK_RET( m_dispatcher, _T("failed to create IO dispatcher") ); + return; m_dispatcher->RegisterFD ( - m_wakeupPipe.GetReadFd(), - &m_wakeupPipe, + m_wakeupPipe->GetReadFd(), + m_wakeupPipe, wxFDIO_INPUT ); } +wxConsoleEventLoop::~wxConsoleEventLoop() +{ + if ( m_wakeupPipe ) + { + if ( m_dispatcher ) + { + m_dispatcher->UnregisterFD(m_wakeupPipe->GetReadFd()); + } + + delete m_wakeupPipe; + } +} + //----------------------------------------------------------------------------- -// events dispatch and loop handling +// adding & removing sources //----------------------------------------------------------------------------- -bool wxConsoleEventLoop::Pending() const +#if wxUSE_EVENTLOOP_SOURCE + +// This class is a temporary bridge between event loop sources and +// FDIODispatcher. It is going to be removed soon, when all subject interfaces +// are modified +class wxFDIOEventLoopSourceHandler : public wxFDIOHandler { - return wxTheApp->HasPendingEvents(); +public: + wxFDIOEventLoopSourceHandler(wxEventLoopSourceHandler* handler) : + m_impl(handler) { } + + virtual void OnReadWaiting() + { + m_impl->OnReadWaiting(); + } + virtual void OnWriteWaiting() + { + m_impl->OnWriteWaiting(); + } + + virtual void OnExceptionWaiting() + { + m_impl->OnExceptionWaiting(); + } + +protected: + wxEventLoopSourceHandler* m_impl; +}; + +wxEventLoopSource * +wxConsoleEventLoop::AddSourceForFD(int fd, + wxEventLoopSourceHandler *handler, + int flags) +{ + wxCHECK_MSG( fd != -1, NULL, "can't monitor invalid fd" ); + + wxLogTrace(wxTRACE_EVT_SOURCE, + "Adding event loop source for fd=%d", fd); + + // we need a bridge to wxFDIODispatcher + // + // TODO: refactor the code so that only wxEventLoopSourceHandler is used + wxScopedPtr + fdioHandler(new wxFDIOEventLoopSourceHandler(handler)); + + if ( !m_dispatcher->RegisterFD(fd, fdioHandler.get(), flags) ) + return NULL; + + return new wxUnixEventLoopSource(m_dispatcher, fdioHandler.release(), + fd, handler, flags); } -bool wxConsoleEventLoop::Dispatch() +wxUnixEventLoopSource::~wxUnixEventLoopSource() { - wxTheApp->ProcessPendingEvents(); - return true; + wxLogTrace(wxTRACE_EVT_SOURCE, + "Removing event loop source for fd=%d", m_fd); + + m_dispatcher->UnregisterFD(m_fd); + + delete m_fdioHandler; } -void wxConsoleEventLoop::WakeUp() +#endif // wxUSE_EVENTLOOP_SOURCE + +//----------------------------------------------------------------------------- +// events dispatch and loop handling +//----------------------------------------------------------------------------- + +bool wxConsoleEventLoop::Pending() const { - m_wakeupPipe.WakeUp(); + if ( m_dispatcher->HasPending() ) + return true; + +#if wxUSE_TIMER + wxUsecClock_t nextTimer; + if ( wxTimerScheduler::Get().GetNext(&nextTimer) && + !wxMilliClockToLong(nextTimer) ) + return true; +#endif // wxUSE_TIMER + + return false; } -void wxConsoleEventLoop::OnNextIteration() +bool wxConsoleEventLoop::Dispatch() { - // calculate the timeout until the next timer expiration - int timeout; + DispatchTimeout(static_cast( + wxFDIODispatcher::TIMEOUT_INFINITE)); + + return true; +} +int wxConsoleEventLoop::DispatchTimeout(unsigned long timeout) +{ #if wxUSE_TIMER + // check if we need to decrease the timeout to account for a timer wxUsecClock_t nextTimer; if ( wxTimerScheduler::Get().GetNext(&nextTimer) ) { - // timeout is in ms - timeout = (nextTimer / 1000).ToLong(); + unsigned long timeUntilNextTimer = wxMilliClockToLong(nextTimer / 1000); + if ( timeUntilNextTimer < timeout ) + timeout = timeUntilNextTimer; } - else // no timers, we can block forever #endif // wxUSE_TIMER - { - timeout = wxFDIODispatcher::TIMEOUT_INFINITE; - } - m_dispatcher->RunLoop(timeout); + bool hadEvent = m_dispatcher->Dispatch(timeout) > 0; #if wxUSE_TIMER - wxTimerScheduler::Get().NotifyExpired(); -#endif + if ( wxTimerScheduler::Get().NotifyExpired() ) + hadEvent = true; +#endif // wxUSE_TIMER + return hadEvent ? 1 : -1; +} + +void wxConsoleEventLoop::WakeUp() +{ + m_wakeupPipe->WakeUp(); +} + +void wxConsoleEventLoop::OnNextIteration() +{ // call the signal handlers for any signals we caught recently wxTheApp->CheckSignal(); } + +wxEventLoopBase *wxConsoleAppTraits::CreateEventLoop() +{ + return new wxEventLoop(); +} + #endif // wxUSE_CONSOLE_EVENTLOOP