]> git.saurik.com Git - wxWidgets.git/blame - src/unix/epolldispatcher.cpp
Avoid overflowing the wake up when handling events in Unix console apps.
[wxWidgets.git] / src / unix / epolldispatcher.cpp
CommitLineData
b46b1d59 1///////////////////////////////////////////////////////////////////////////////
80fdcdb9 2// Name: src/unix/epolldispatcher.cpp
b46b1d59
VZ
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
526954c5 8// Licence: wxWindows licence
b46b1d59
VZ
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
9550ee98 22#if wxUSE_EPOLL_DISPATCHER
b46b1d59
VZ
23
24#include "wx/unix/private/epolldispatcher.h"
25#include "wx/unix/private.h"
e169ac81 26#include "wx/stopwatch.h"
b46b1d59 27
b5ef33b2
VS
28#ifndef WX_PRECOMP
29 #include "wx/log.h"
30 #include "wx/intl.h"
31#endif
32
b46b1d59
VZ
33#include <sys/epoll.h>
34#include <errno.h>
98883bca 35#include <unistd.h>
b46b1d59
VZ
36
37#define wxEpollDispatcher_Trace wxT("epolldispatcher")
38
b46b1d59
VZ
39// ============================================================================
40// implementation
41// ============================================================================
42
43// helper: return EPOLLxxx mask corresponding to the given flags (and also log
44// debugging messages about it)
3f65cc75 45static uint32_t GetEpollMask(int flags, int fd)
b46b1d59 46{
3f65cc75
VZ
47 wxUnusedVar(fd); // unused if wxLogTrace() disabled
48
b46b1d59
VZ
49 uint32_t ep = 0;
50
51 if ( flags & wxFDIO_INPUT )
52 {
53 ep |= EPOLLIN;
54 wxLogTrace(wxEpollDispatcher_Trace,
9a83f860 55 wxT("Registered fd %d for input events"), fd);
b46b1d59
VZ
56 }
57
58 if ( flags & wxFDIO_OUTPUT )
59 {
60 ep |= EPOLLOUT;
61 wxLogTrace(wxEpollDispatcher_Trace,
9a83f860 62 wxT("Registered fd %d for output events"), fd);
b46b1d59
VZ
63 }
64
65 if ( flags & wxFDIO_EXCEPTION )
66 {
67 ep |= EPOLLERR | EPOLLHUP;
68 wxLogTrace(wxEpollDispatcher_Trace,
9a83f860 69 wxT("Registered fd %d for exceptional events"), fd);
b46b1d59
VZ
70 }
71
72 return ep;
73}
74
75// ----------------------------------------------------------------------------
76// wxEpollDispatcher
77// ----------------------------------------------------------------------------
78
5e1eac14
VZ
79/* static */
80wxEpollDispatcher *wxEpollDispatcher::Create()
b46b1d59 81{
5e1eac14
VZ
82 int epollDescriptor = epoll_create(1024);
83 if ( epollDescriptor == -1 )
b46b1d59
VZ
84 {
85 wxLogSysError(_("Failed to create epoll descriptor"));
5e1eac14 86 return NULL;
b46b1d59 87 }
2804f77d 88 wxLogTrace(wxEpollDispatcher_Trace,
9a83f860 89 wxT("Epoll fd %d created"), epollDescriptor);
5e1eac14
VZ
90 return new wxEpollDispatcher(epollDescriptor);
91}
92
93wxEpollDispatcher::wxEpollDispatcher(int epollDescriptor)
94{
9a83f860 95 wxASSERT_MSG( epollDescriptor != -1, wxT("invalid descriptor") );
5e1eac14
VZ
96
97 m_epollDescriptor = epollDescriptor;
b46b1d59
VZ
98}
99
d31a4a84
VZ
100wxEpollDispatcher::~wxEpollDispatcher()
101{
102 if ( close(m_epollDescriptor) != 0 )
103 {
104 wxLogSysError(_("Error closing epoll descriptor"));
105 }
106}
107
b46b1d59
VZ
108bool wxEpollDispatcher::RegisterFD(int fd, wxFDIOHandler* handler, int flags)
109{
b46b1d59
VZ
110 epoll_event ev;
111 ev.events = GetEpollMask(flags, fd);
112 ev.data.ptr = handler;
113
114 const int ret = epoll_ctl(m_epollDescriptor, EPOLL_CTL_ADD, fd, &ev);
115 if ( ret != 0 )
116 {
117 wxLogSysError(_("Failed to add descriptor %d to epoll descriptor %d"),
118 fd, m_epollDescriptor);
119
120 return false;
121 }
2804f77d 122 wxLogTrace(wxEpollDispatcher_Trace,
9a83f860 123 wxT("Added fd %d (handler %p) to epoll %d"), fd, handler, m_epollDescriptor);
b46b1d59
VZ
124
125 return true;
126}
127
128bool wxEpollDispatcher::ModifyFD(int fd, wxFDIOHandler* handler, int flags)
129{
b46b1d59
VZ
130 epoll_event ev;
131 ev.events = GetEpollMask(flags, fd);
132 ev.data.ptr = handler;
133
134 const int ret = epoll_ctl(m_epollDescriptor, EPOLL_CTL_MOD, fd, &ev);
135 if ( ret != 0 )
136 {
137 wxLogSysError(_("Failed to modify descriptor %d in epoll descriptor %d"),
138 fd, m_epollDescriptor);
139
140 return false;
141 }
142
2804f77d 143 wxLogTrace(wxEpollDispatcher_Trace,
9a83f860 144 wxT("Modified fd %d (handler: %p) on epoll %d"), fd, handler, m_epollDescriptor);
b46b1d59
VZ
145 return true;
146}
147
af57c51a 148bool wxEpollDispatcher::UnregisterFD(int fd)
b46b1d59 149{
b46b1d59
VZ
150 epoll_event ev;
151 ev.events = 0;
152 ev.data.ptr = NULL;
153
154 if ( epoll_ctl(m_epollDescriptor, EPOLL_CTL_DEL, fd, &ev) != 0 )
155 {
156 wxLogSysError(_("Failed to unregister descriptor %d from epoll descriptor %d"),
157 fd, m_epollDescriptor);
158 }
2804f77d 159 wxLogTrace(wxEpollDispatcher_Trace,
9a83f860 160 wxT("removed fd %d from %d"), fd, m_epollDescriptor);
ad8d42f8 161 return true;
b46b1d59
VZ
162}
163
a12698ab
VZ
164int
165wxEpollDispatcher::DoPoll(epoll_event *events, int numEvents, int timeout) const
b46b1d59 166{
e169ac81
VZ
167 // the code below relies on TIMEOUT_INFINITE being -1 so that we can pass
168 // timeout value directly to epoll_wait() which interprets -1 as meaning to
169 // wait forever and would need to be changed if the value of
170 // TIMEOUT_INFINITE ever changes
171 wxCOMPILE_TIME_ASSERT( TIMEOUT_INFINITE == -1, UpdateThisCode );
b46b1d59 172
e169ac81 173 wxMilliClock_t timeEnd;
a12698ab 174 if ( timeout > 0 )
e169ac81
VZ
175 timeEnd = wxGetLocalTimeMillis();
176
177 int rc;
178 for ( ;; )
b46b1d59 179 {
a12698ab 180 rc = epoll_wait(m_epollDescriptor, events, numEvents, timeout);
e169ac81
VZ
181 if ( rc != -1 || errno != EINTR )
182 break;
183
184 // we got interrupted, update the timeout and restart
a12698ab
VZ
185 if ( timeout > 0 )
186 {
187 timeout = wxMilliClockToLong(timeEnd - wxGetLocalTimeMillis());
188 if ( timeout < 0 )
189 return 0;
190 }
e169ac81
VZ
191 }
192
a12698ab
VZ
193 return rc;
194}
195
196bool wxEpollDispatcher::HasPending() const
197{
198 epoll_event event;
a7132f4b
VZ
199
200 // NB: it's not really clear if epoll_wait() can return a number greater
201 // than the number of events passed to it but just in case it can, use
202 // >= instead of == here, see #10397
fa00c4e3 203 return DoPoll(&event, 1, 0) >= 1;
a12698ab
VZ
204}
205
206int wxEpollDispatcher::Dispatch(int timeout)
207{
208 epoll_event events[16];
209
210 const int rc = DoPoll(events, WXSIZEOF(events), timeout);
211
e169ac81
VZ
212 if ( rc == -1 )
213 {
214 wxLogSysError(_("Waiting for IO on epoll descriptor %d failed"),
215 m_epollDescriptor);
a12698ab 216 return -1;
b46b1d59
VZ
217 }
218
a12698ab 219 int numEvents = 0;
e169ac81 220 for ( epoll_event *p = events; p < events + rc; p++ )
b46b1d59
VZ
221 {
222 wxFDIOHandler * const handler = (wxFDIOHandler *)(p->data.ptr);
223 if ( !handler )
224 {
9a83f860 225 wxFAIL_MSG( wxT("NULL handler in epoll_event?") );
b46b1d59
VZ
226 continue;
227 }
228
1a781247
VZ
229 // note that for compatibility with wxSelectDispatcher we call
230 // OnReadWaiting() on EPOLLHUP as this is what epoll_wait() returns
231 // when the write end of a pipe is closed while with select() the
232 // remaining pipe end becomes ready for reading when this happens
233 if ( p->events & (EPOLLIN | EPOLLHUP) )
b46b1d59 234 handler->OnReadWaiting();
7523de90 235 else if ( p->events & EPOLLOUT )
b46b1d59 236 handler->OnWriteWaiting();
1a781247 237 else if ( p->events & EPOLLERR )
b46b1d59 238 handler->OnExceptionWaiting();
5a557d1e
VZ
239 else
240 continue;
241
a12698ab 242 numEvents++;
b46b1d59 243 }
5a557d1e 244
a12698ab 245 return numEvents;
b46b1d59
VZ
246}
247
a1873279 248#endif // wxUSE_EPOLL_DISPATCHER