1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/selectdispatcher.cpp
3 // Purpose: implements dispatcher for select() call
4 // Author: Lukasz Michalski and Vadim Zeitlin
5 // Created: December 2006
6 // Copyright: (c) 2006 Lukasz Michalski
7 // Licence: wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
9
10 // ============================================================================
11 // declarations
12 // ============================================================================
13
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17
18 // for compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20
21
22 #if wxUSE_SELECT_DISPATCHER
23
24 #include "wx/private/selectdispatcher.h"
25 #include "wx/unix/private.h"
26
27 #ifndef WX_PRECOMP
28 #include "wx/hash.h"
29 #include "wx/log.h"
30 #include "wx/intl.h"
31 #endif
32
33 #include <errno.h>
34
35 #define wxSelectDispatcher_Trace wxT("selectdispatcher")
36
37 // ============================================================================
38 // implementation
39 // ============================================================================
40
41 // ----------------------------------------------------------------------------
42 // wxSelectSets
43 // ----------------------------------------------------------------------------
44
45 int wxSelectSets::ms_flags[wxSelectSets::Max] =
46 {
47 wxFDIO_INPUT,
48 wxFDIO_OUTPUT,
49 wxFDIO_EXCEPTION,
50 };
51
52 const char *wxSelectSets::ms_names[wxSelectSets::Max] =
53 {
54 "input",
55 "output",
56 "exceptional",
57 };
58
59 wxSelectSets::Callback wxSelectSets::ms_handlers[wxSelectSets::Max] =
60 {
61 &wxFDIOHandler::OnReadWaiting,
62 &wxFDIOHandler::OnWriteWaiting,
63 &wxFDIOHandler::OnExceptionWaiting,
64 };
65
wxSelectSets()66 wxSelectSets::wxSelectSets()
67 {
68 for ( int n = 0; n < Max; n++ )
69 {
70 wxFD_ZERO(&m_fds[n]);
71 }
72 }
73
HasFD(int fd) const74 bool wxSelectSets::HasFD(int fd) const
75 {
76 for ( int n = 0; n < Max; n++ )
77 {
78 if ( wxFD_ISSET(fd, const_cast<fd_set*>(&m_fds[n])) )
79 return true;
80 }
81
82 return false;
83 }
84
SetFD(int fd,int flags)85 bool wxSelectSets::SetFD(int fd, int flags)
86 {
87 wxCHECK_MSG( fd >= 0, false, wxT("invalid descriptor") );
88
89 for ( int n = 0; n < Max; n++ )
90 {
91 if ( flags & ms_flags[n] )
92 {
93 wxFD_SET(fd, &m_fds[n]);
94 }
95 else if ( wxFD_ISSET(fd, (fd_set*) &m_fds[n]) )
96 {
97 wxFD_CLR(fd, &m_fds[n]);
98 }
99 }
100
101 return true;
102 }
103
Select(int nfds,struct timeval * tv)104 int wxSelectSets::Select(int nfds, struct timeval *tv)
105 {
106 return select(nfds, &m_fds[Read], &m_fds[Write], &m_fds[Except], tv);
107 }
108
Handle(int fd,wxFDIOHandler & handler) const109 bool wxSelectSets::Handle(int fd, wxFDIOHandler& handler) const
110 {
111 for ( int n = 0; n < Max; n++ )
112 {
113 if ( wxFD_ISSET(fd, const_cast<fd_set*>(&m_fds[n])) )
114 {
115 wxLogTrace(wxSelectDispatcher_Trace,
116 wxT("Got %s event on fd %d"), ms_names[n], fd);
117 (handler.*ms_handlers[n])();
118 // callback can modify sets and destroy handler
119 // this forces that one event can be processed at one time
120 return true;
121 }
122 }
123
124 return false;
125 }
126
127 // ----------------------------------------------------------------------------
128 // wxSelectDispatcher
129 // ----------------------------------------------------------------------------
130
RegisterFD(int fd,wxFDIOHandler * handler,int flags)131 bool wxSelectDispatcher::RegisterFD(int fd, wxFDIOHandler *handler, int flags)
132 {
133 wxCRIT_SECT_LOCKER(lock, m_cs);
134
135 if ( !wxMappedFDIODispatcher::RegisterFD(fd, handler, flags) )
136 return false;
137
138 if ( !m_sets.SetFD(fd, flags) )
139 return false;
140
141 if ( fd > m_maxFD )
142 m_maxFD = fd;
143
144 wxLogTrace(wxSelectDispatcher_Trace,
145 wxT("Registered fd %d: input:%d, output:%d, exceptional:%d"), fd, (flags & wxFDIO_INPUT) == wxFDIO_INPUT, (flags & wxFDIO_OUTPUT), (flags & wxFDIO_EXCEPTION) == wxFDIO_EXCEPTION);
146 return true;
147 }
148
ModifyFD(int fd,wxFDIOHandler * handler,int flags)149 bool wxSelectDispatcher::ModifyFD(int fd, wxFDIOHandler *handler, int flags)
150 {
151 wxCRIT_SECT_LOCKER(lock, m_cs);
152
153 if ( !wxMappedFDIODispatcher::ModifyFD(fd, handler, flags) )
154 return false;
155
156 wxASSERT_MSG( fd <= m_maxFD, wxT("logic error: registered fd > m_maxFD?") );
157
158 wxLogTrace(wxSelectDispatcher_Trace,
159 wxT("Modified fd %d: input:%d, output:%d, exceptional:%d"), fd, (flags & wxFDIO_INPUT) == wxFDIO_INPUT, (flags & wxFDIO_OUTPUT) == wxFDIO_OUTPUT, (flags & wxFDIO_EXCEPTION) == wxFDIO_EXCEPTION);
160 return m_sets.SetFD(fd, flags);
161 }
162
UnregisterFD(int fd)163 bool wxSelectDispatcher::UnregisterFD(int fd)
164 {
165 wxCRIT_SECT_LOCKER(lock, m_cs);
166
167 m_sets.ClearFD(fd);
168
169 if ( !wxMappedFDIODispatcher::UnregisterFD(fd) )
170 return false;
171
172 // remove the handler if we don't need it any more
173 if ( !m_sets.HasFD(fd) )
174 {
175 if ( fd == m_maxFD )
176 {
177 // need to find new max fd
178 m_maxFD = -1;
179 for ( wxFDIOHandlerMap::const_iterator it = m_handlers.begin();
180 it != m_handlers.end();
181 ++it )
182 {
183 if ( it->first > m_maxFD )
184 {
185 m_maxFD = it->first;
186 }
187 }
188 }
189 }
190
191 wxLogTrace(wxSelectDispatcher_Trace,
192 wxT("Removed fd %d, current max: %d"), fd, m_maxFD);
193 return true;
194 }
195
ProcessSets(const wxSelectSets & sets)196 int wxSelectDispatcher::ProcessSets(const wxSelectSets& sets)
197 {
198 int numEvents = 0;
199 for ( int fd = 0; fd <= m_maxFD; fd++ )
200 {
201 if ( !sets.HasFD(fd) )
202 continue;
203
204 wxFDIOHandler * const handler = FindHandler(fd);
205 if ( !handler )
206 {
207 wxFAIL_MSG( wxT("NULL handler in wxSelectDispatcher?") );
208 continue;
209 }
210
211 if ( sets.Handle(fd, *handler) )
212 numEvents++;
213 }
214
215 return numEvents;
216 }
217
DoSelect(wxSelectSets & sets,int timeout) const218 int wxSelectDispatcher::DoSelect(wxSelectSets& sets, int timeout) const
219 {
220 struct timeval tv,
221 *ptv;
222 if ( timeout != TIMEOUT_INFINITE )
223 {
224 ptv = &tv;
225 tv.tv_sec = timeout / 1000;
226 tv.tv_usec = (timeout % 1000)*1000;
227 }
228 else // no timeout
229 {
230 ptv = NULL;
231 }
232
233 int ret = sets.Select(m_maxFD + 1, ptv);
234
235 // TODO: we need to restart select() in this case but for now just return
236 // as if timeout expired
237 if ( ret == -1 && errno == EINTR )
238 ret = 0;
239
240 return ret;
241 }
242
HasPending() const243 bool wxSelectDispatcher::HasPending() const
244 {
245 wxSelectSets sets(m_sets);
246 return DoSelect(sets, 0) > 0;
247 }
248
Dispatch(int timeout)249 int wxSelectDispatcher::Dispatch(int timeout)
250 {
251 wxSelectSets sets(m_sets);
252 switch ( DoSelect(sets, timeout) )
253 {
254 case -1:
255 wxLogSysError(_("Failed to monitor I/O channels"));
256 return -1;
257
258 case 0:
259 // timeout expired without anything happening
260 return 0;
261
262 default:
263 return ProcessSets(sets);
264 }
265 }
266
267 #endif // wxUSE_SELECT_DISPATCHER
268