properly restart epoll_wait() if it got interrupted by a signal
[wxWidgets.git] / src / unix / epolldispatcher.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/epolldispatcher.cpp
3 // Purpose: implements dispatcher for epoll_wait() call
4 // Author: Lukasz Michalski
5 // Created: April 2007
6 // RCS-ID: $Id$
7 // Copyright: (c) 2007 Lukasz Michalski
8 // License: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #if wxUSE_EPOLL_DISPATCHER
23
24 #include "wx/unix/private/epolldispatcher.h"
25 #include "wx/unix/private.h"
26 #include "wx/stopwatch.h"
27
28 #ifndef WX_PRECOMP
29 #include "wx/log.h"
30 #include "wx/intl.h"
31 #endif
32
33 #include <sys/epoll.h>
34 #include <errno.h>
35 #include <unistd.h>
36
37 #define wxEpollDispatcher_Trace wxT("epolldispatcher")
38
39 // ============================================================================
40 // implementation
41 // ============================================================================
42
43 // helper: return EPOLLxxx mask corresponding to the given flags (and also log
44 // debugging messages about it)
45 static uint32_t GetEpollMask(int flags, int WXUNUSED_UNLESS_DEBUG(fd))
46 {
47 uint32_t ep = 0;
48
49 if ( flags & wxFDIO_INPUT )
50 {
51 ep |= EPOLLIN;
52 wxLogTrace(wxEpollDispatcher_Trace,
53 _T("Registered fd %d for input events"), fd);
54 }
55
56 if ( flags & wxFDIO_OUTPUT )
57 {
58 ep |= EPOLLOUT;
59 wxLogTrace(wxEpollDispatcher_Trace,
60 _T("Registered fd %d for output events"), fd);
61 }
62
63 if ( flags & wxFDIO_EXCEPTION )
64 {
65 ep |= EPOLLERR | EPOLLHUP;
66 wxLogTrace(wxEpollDispatcher_Trace,
67 _T("Registered fd %d for exceptional events"), fd);
68 }
69
70 return ep;
71 }
72
73 // ----------------------------------------------------------------------------
74 // wxEpollDispatcher
75 // ----------------------------------------------------------------------------
76
77 /* static */
78 wxEpollDispatcher *wxEpollDispatcher::Create()
79 {
80 int epollDescriptor = epoll_create(1024);
81 if ( epollDescriptor == -1 )
82 {
83 wxLogSysError(_("Failed to create epoll descriptor"));
84 return NULL;
85 }
86 wxLogTrace(wxEpollDispatcher_Trace,
87 _T("Epoll fd %d created"), epollDescriptor);
88 return new wxEpollDispatcher(epollDescriptor);
89 }
90
91 wxEpollDispatcher::wxEpollDispatcher(int epollDescriptor)
92 {
93 wxASSERT_MSG( epollDescriptor != -1, _T("invalid descriptor") );
94
95 m_epollDescriptor = epollDescriptor;
96 }
97
98 wxEpollDispatcher::~wxEpollDispatcher()
99 {
100 if ( close(m_epollDescriptor) != 0 )
101 {
102 wxLogSysError(_("Error closing epoll descriptor"));
103 }
104 }
105
106 bool wxEpollDispatcher::RegisterFD(int fd, wxFDIOHandler* handler, int flags)
107 {
108 epoll_event ev;
109 ev.events = GetEpollMask(flags, fd);
110 ev.data.ptr = handler;
111
112 const int ret = epoll_ctl(m_epollDescriptor, EPOLL_CTL_ADD, fd, &ev);
113 if ( ret != 0 )
114 {
115 wxLogSysError(_("Failed to add descriptor %d to epoll descriptor %d"),
116 fd, m_epollDescriptor);
117
118 return false;
119 }
120 wxLogTrace(wxEpollDispatcher_Trace,
121 _T("Added fd %d (handler %p) to epoll %d"), fd, handler, m_epollDescriptor);
122
123 return true;
124 }
125
126 bool wxEpollDispatcher::ModifyFD(int fd, wxFDIOHandler* handler, int flags)
127 {
128 epoll_event ev;
129 ev.events = GetEpollMask(flags, fd);
130 ev.data.ptr = handler;
131
132 const int ret = epoll_ctl(m_epollDescriptor, EPOLL_CTL_MOD, fd, &ev);
133 if ( ret != 0 )
134 {
135 wxLogSysError(_("Failed to modify descriptor %d in epoll descriptor %d"),
136 fd, m_epollDescriptor);
137
138 return false;
139 }
140
141 wxLogTrace(wxEpollDispatcher_Trace,
142 _T("Modified fd %d (handler: %p) on epoll %d"), fd, handler, m_epollDescriptor);
143 return true;
144 }
145
146 bool wxEpollDispatcher::UnregisterFD(int fd)
147 {
148 epoll_event ev;
149 ev.events = 0;
150 ev.data.ptr = NULL;
151
152 if ( epoll_ctl(m_epollDescriptor, EPOLL_CTL_DEL, fd, &ev) != 0 )
153 {
154 wxLogSysError(_("Failed to unregister descriptor %d from epoll descriptor %d"),
155 fd, m_epollDescriptor);
156 }
157 wxLogTrace(wxEpollDispatcher_Trace,
158 _T("removed fd %d from %d"), fd, m_epollDescriptor);
159 return true;
160 }
161
162 bool wxEpollDispatcher::Dispatch(int timeout)
163 {
164 epoll_event events[16];
165
166 // the code below relies on TIMEOUT_INFINITE being -1 so that we can pass
167 // timeout value directly to epoll_wait() which interprets -1 as meaning to
168 // wait forever and would need to be changed if the value of
169 // TIMEOUT_INFINITE ever changes
170 wxCOMPILE_TIME_ASSERT( TIMEOUT_INFINITE == -1, UpdateThisCode );
171
172 wxMilliClock_t timeEnd;
173 if ( timeout != -1 )
174 timeEnd = wxGetLocalTimeMillis();
175
176 int rc;
177 for ( ;; )
178 {
179 rc = epoll_wait(m_epollDescriptor, events, WXSIZEOF(events), timeout);
180 if ( rc != -1 || errno != EINTR )
181 break;
182
183 // we got interrupted, update the timeout and restart
184 if ( timeout == -1 )
185 continue;
186
187 timeout = wxMilliClockToLong(timeEnd - wxGetLocalTimeMillis());
188 if ( timeout < 0 )
189 return false;
190 }
191
192 if ( rc == -1 )
193 {
194 wxLogSysError(_("Waiting for IO on epoll descriptor %d failed"),
195 m_epollDescriptor);
196 return false;
197 }
198
199 bool gotEvents = false;
200 for ( epoll_event *p = events; p < events + rc; p++ )
201 {
202 wxFDIOHandler * const handler = (wxFDIOHandler *)(p->data.ptr);
203 if ( !handler )
204 {
205 wxFAIL_MSG( _T("NULL handler in epoll_event?") );
206 continue;
207 }
208
209 // note that for compatibility with wxSelectDispatcher we call
210 // OnReadWaiting() on EPOLLHUP as this is what epoll_wait() returns
211 // when the write end of a pipe is closed while with select() the
212 // remaining pipe end becomes ready for reading when this happens
213 if ( p->events & (EPOLLIN | EPOLLHUP) )
214 handler->OnReadWaiting();
215 else if ( p->events & EPOLLOUT )
216 handler->OnWriteWaiting();
217 else if ( p->events & EPOLLERR )
218 handler->OnExceptionWaiting();
219 else
220 continue;
221
222 gotEvents = true;
223 }
224
225 return gotEvents;
226 }
227
228 #endif // wxUSE_EPOLL_DISPATCHER