1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/unix/evtloopunix.cpp
3 // Purpose: wxEventLoop implementation
4 // Author: Lukasz Michalski (lm@zork.pl)
7 // Copyright: (c) 2006 Zork Lukasz Michalski
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // ===========================================================================
13 // ===========================================================================
15 // ---------------------------------------------------------------------------
17 // ---------------------------------------------------------------------------
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
22 #if wxUSE_CONSOLE_EVENTLOOP
24 #include "wx/evtloop.h"
32 #include "wx/apptrait.h"
33 #include "wx/scopedptr.h"
34 #include "wx/thread.h"
35 #include "wx/module.h"
36 #include "wx/unix/pipe.h"
37 #include "wx/unix/private/timer.h"
38 #include "wx/unix/private/epolldispatcher.h"
39 #include "wx/private/selectdispatcher.h"
41 #if wxUSE_EVENTLOOP_SOURCE
42 #include "wx/evtloopsrc.h"
43 #endif // wxUSE_EVENTLOOP_SOURCE
45 #define TRACE_EVENTS wxT("events")
47 // ===========================================================================
48 // wxEventLoop::PipeIOHandler implementation
49 // ===========================================================================
54 // pipe used for wake up messages: when a child thread wants to wake up
55 // the event loop in the main thread it writes to this pipe
56 class PipeIOHandler
: public wxFDIOHandler
59 // default ctor does nothing, call Create() to really initialize the
61 PipeIOHandler() : m_pipeIsEmpty(true) { }
65 // this method can be, and normally is, called from another thread
68 int GetReadFd() { return m_pipe
[wxPipe::Read
]; }
70 // implement wxFDIOHandler pure virtual methods
71 virtual void OnReadWaiting();
72 virtual void OnWriteWaiting() { }
73 virtual void OnExceptionWaiting() { }
78 // Protects access to m_pipeIsEmpty.
79 wxCriticalSection m_pipeLock
;
81 // This flag is set to true after writing to the pipe and reset to false
82 // after reading from it in the main thread. Having it allows us to avoid
83 // overflowing the pipe with too many writes if the main thread can't keep
84 // up with reading from it.
88 // ----------------------------------------------------------------------------
90 // ----------------------------------------------------------------------------
92 bool PipeIOHandler::Create()
94 if ( !m_pipe
.Create() )
96 wxLogError(_("Failed to create wake up pipe used by event loop."));
101 if ( !m_pipe
.MakeNonBlocking(wxPipe::Read
) )
103 wxLogSysError(_("Failed to switch wake up pipe to non-blocking mode"));
107 wxLogTrace(TRACE_EVENTS
, wxT("Wake up pipe (%d, %d) created"),
108 m_pipe
[wxPipe::Read
], m_pipe
[wxPipe::Write
]);
113 // ----------------------------------------------------------------------------
115 // ----------------------------------------------------------------------------
117 void PipeIOHandler::WakeUp()
119 wxCriticalSectionLocker
lock(m_pipeLock
);
121 // No need to do anything if the pipe already contains something.
122 if ( !m_pipeIsEmpty
)
125 if ( write(m_pipe
[wxPipe::Write
], "s", 1) != 1 )
127 // don't use wxLog here, we can be in another thread and this could
128 // result in dead locks
129 perror("write(wake up pipe)");
133 // We just wrote to it, so it's not empty any more.
134 m_pipeIsEmpty
= false;
138 void PipeIOHandler::OnReadWaiting()
140 // got wakeup from child thread, remove the data that provoked it from the
143 wxCriticalSectionLocker
lock(m_pipeLock
);
148 const int size
= read(GetReadFd(), buf
, WXSIZEOF(buf
));
152 wxASSERT_MSG( size
== 1, "Too many writes to wake-up pipe?" );
157 if ( size
== 0 || (size
== -1 && errno
== EAGAIN
) )
159 // No data available, not an error (but still surprising,
164 if ( errno
== EINTR
)
166 // We were interrupted, try again.
170 wxLogSysError(_("Failed to read from wake-up pipe"));
175 // The pipe is empty now, so future calls to WakeUp() would need to write
177 m_pipeIsEmpty
= true;
179 // writing to the wake up pipe will make wxConsoleEventLoop return from
180 // wxFDIODispatcher::Dispatch() it might be currently blocking in, nothing
181 // else needs to be done
184 } // namespace wxPrivate
186 // ===========================================================================
187 // wxEventLoop implementation
188 // ===========================================================================
190 //-----------------------------------------------------------------------------
192 //-----------------------------------------------------------------------------
194 wxConsoleEventLoop::wxConsoleEventLoop()
196 m_wakeupPipe
= new wxPrivate::PipeIOHandler();
197 if ( !m_wakeupPipe
->Create() )
199 wxDELETE(m_wakeupPipe
);
204 m_dispatcher
= wxFDIODispatcher::Get();
208 m_dispatcher
->RegisterFD
210 m_wakeupPipe
->GetReadFd(),
216 wxConsoleEventLoop::~wxConsoleEventLoop()
222 m_dispatcher
->UnregisterFD(m_wakeupPipe
->GetReadFd());
229 //-----------------------------------------------------------------------------
230 // adding & removing sources
231 //-----------------------------------------------------------------------------
233 #if wxUSE_EVENTLOOP_SOURCE
235 // This class is a temporary bridge between event loop sources and
236 // FDIODispatcher. It is going to be removed soon, when all subject interfaces
238 class wxFDIOEventLoopSourceHandler
: public wxFDIOHandler
241 wxFDIOEventLoopSourceHandler(wxEventLoopSourceHandler
* handler
) :
244 virtual void OnReadWaiting()
246 m_impl
->OnReadWaiting();
248 virtual void OnWriteWaiting()
250 m_impl
->OnWriteWaiting();
253 virtual void OnExceptionWaiting()
255 m_impl
->OnExceptionWaiting();
259 wxEventLoopSourceHandler
* m_impl
;
263 wxConsoleEventLoop::AddSourceForFD(int fd
,
264 wxEventLoopSourceHandler
*handler
,
267 wxCHECK_MSG( fd
!= -1, NULL
, "can't monitor invalid fd" );
269 wxLogTrace(wxTRACE_EVT_SOURCE
,
270 "Adding event loop source for fd=%d", fd
);
272 // we need a bridge to wxFDIODispatcher
274 // TODO: refactor the code so that only wxEventLoopSourceHandler is used
275 wxScopedPtr
<wxFDIOHandler
>
276 fdioHandler(new wxFDIOEventLoopSourceHandler(handler
));
278 if ( !m_dispatcher
->RegisterFD(fd
, fdioHandler
.get(), flags
) )
281 return new wxUnixEventLoopSource(m_dispatcher
, fdioHandler
.release(),
285 wxUnixEventLoopSource::~wxUnixEventLoopSource()
287 wxLogTrace(wxTRACE_EVT_SOURCE
,
288 "Removing event loop source for fd=%d", m_fd
);
290 m_dispatcher
->UnregisterFD(m_fd
);
292 delete m_fdioHandler
;
295 #endif // wxUSE_EVENTLOOP_SOURCE
297 //-----------------------------------------------------------------------------
298 // events dispatch and loop handling
299 //-----------------------------------------------------------------------------
301 bool wxConsoleEventLoop::Pending() const
303 if ( m_dispatcher
->HasPending() )
307 wxUsecClock_t nextTimer
;
308 if ( wxTimerScheduler::Get().GetNext(&nextTimer
) &&
309 !wxMilliClockToLong(nextTimer
) )
311 #endif // wxUSE_TIMER
316 bool wxConsoleEventLoop::Dispatch()
318 DispatchTimeout(static_cast<unsigned long>(
319 wxFDIODispatcher::TIMEOUT_INFINITE
));
324 int wxConsoleEventLoop::DispatchTimeout(unsigned long timeout
)
327 // check if we need to decrease the timeout to account for a timer
328 wxUsecClock_t nextTimer
;
329 if ( wxTimerScheduler::Get().GetNext(&nextTimer
) )
331 unsigned long timeUntilNextTimer
= wxMilliClockToLong(nextTimer
/ 1000);
332 if ( timeUntilNextTimer
< timeout
)
333 timeout
= timeUntilNextTimer
;
335 #endif // wxUSE_TIMER
337 bool hadEvent
= m_dispatcher
->Dispatch(timeout
) > 0;
340 if ( wxTimerScheduler::Get().NotifyExpired() )
342 #endif // wxUSE_TIMER
344 return hadEvent
? 1 : -1;
347 void wxConsoleEventLoop::WakeUp()
349 m_wakeupPipe
->WakeUp();
352 void wxConsoleEventLoop::OnNextIteration()
354 // call the signal handlers for any signals we caught recently
355 wxTheApp
->CheckSignal();
359 wxEventLoopBase
*wxConsoleAppTraits::CreateEventLoop()
361 return new wxEventLoop();
364 #endif // wxUSE_CONSOLE_EVENTLOOP