changed wxFDIODispatcher::UnregisterFD() to take only fd, without flags, and unregist...
[wxWidgets.git] / src / common / selectdispatcher.cpp
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 // RCS-ID: $Id$
7 // Copyright: (c) 2006 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_SELECT_DISPATCHER
23
24 #include "wx/private/selectdispatcher.h"
25 #include "wx/module.h"
26 #include "wx/timer.h"
27 #include "wx/unix/private.h"
28
29 #ifndef WX_PRECOMP
30 #include "wx/hash.h"
31 #include "wx/log.h"
32 #include "wx/intl.h"
33 #endif
34
35 #ifdef HAVE_SYS_SELECT_H
36 #include <sys/select.h>
37 #endif
38
39 #include <errno.h>
40
41 #define wxSelectDispatcher_Trace wxT("selectdispatcher")
42
43 // ============================================================================
44 // implementation
45 // ============================================================================
46
47 // ----------------------------------------------------------------------------
48 // wxSelectSets
49 // ----------------------------------------------------------------------------
50
51 int wxSelectSets::ms_flags[wxSelectSets::Max] =
52 {
53 wxFDIO_INPUT,
54 wxFDIO_OUTPUT,
55 wxFDIO_EXCEPTION,
56 };
57
58 const char *wxSelectSets::ms_names[wxSelectSets::Max] =
59 {
60 "input",
61 "output",
62 "exceptional",
63 };
64
65 wxSelectSets::Callback wxSelectSets::ms_handlers[wxSelectSets::Max] =
66 {
67 &wxFDIOHandler::OnReadWaiting,
68 &wxFDIOHandler::OnWriteWaiting,
69 &wxFDIOHandler::OnExceptionWaiting,
70 };
71
72 wxSelectSets::wxSelectSets()
73 {
74 for ( int n = 0; n < Max; n++ )
75 {
76 wxFD_ZERO(&m_fds[n]);
77 }
78 }
79
80 bool wxSelectSets::HasFD(int fd) const
81 {
82 for ( int n = 0; n < Max; n++ )
83 {
84 if ( wxFD_ISSET(fd, (fd_set*) &m_fds[n]) )
85 return true;
86 }
87
88 return false;
89 }
90
91 bool wxSelectSets::SetFD(int fd, int flags)
92 {
93 wxCHECK_MSG( fd >= 0, false, _T("invalid descriptor") );
94
95 for ( int n = 0; n < Max; n++ )
96 {
97 if ( flags & ms_flags[n] )
98 {
99 wxFD_SET(fd, &m_fds[n]);
100 wxLogTrace(wxSelectDispatcher_Trace,
101 _T("Registered fd %d for %s events"), fd, ms_names[n]);
102 }
103 else if ( wxFD_ISSET(fd, (fd_set*) &m_fds[n]) )
104 {
105 wxFD_CLR(fd, &m_fds[n]);
106 wxLogTrace(wxSelectDispatcher_Trace,
107 _T("Unregistered fd %d from %s events"), fd, ms_names[n]);
108 }
109 }
110
111 return true;
112 }
113
114 int wxSelectSets::Select(int nfds, struct timeval *tv)
115 {
116 return select(nfds, &m_fds[Read], &m_fds[Write], &m_fds[Except], tv);
117 }
118
119 void wxSelectSets::Handle(int fd, wxFDIOHandler& handler) const
120 {
121 for ( int n = 0; n < Max; n++ )
122 {
123 if ( wxFD_ISSET(fd, (fd_set*) &m_fds[n]) )
124 {
125 wxLogTrace(wxSelectDispatcher_Trace,
126 _T("Got %s event on fd %d"), ms_names[n], fd);
127 (handler.*ms_handlers[n])();
128 }
129 }
130 }
131
132 // ----------------------------------------------------------------------------
133 // wxSelectDispatcher
134 // ----------------------------------------------------------------------------
135
136 static wxSelectDispatcher *gs_selectDispatcher = NULL;
137
138 /* static */
139 wxSelectDispatcher *wxSelectDispatcher::Get()
140 {
141 if ( !gs_selectDispatcher )
142 {
143 // the dispatcher should be only created from one thread so it should
144 // be ok to use a global without any protection here
145 gs_selectDispatcher = new wxSelectDispatcher;
146 }
147
148 return gs_selectDispatcher;
149 }
150
151 /* static */
152 void wxSelectDispatcher::DispatchPending()
153 {
154 if ( gs_selectDispatcher )
155 gs_selectDispatcher->RunLoop(0);
156 }
157
158 wxSelectDispatcher::wxSelectDispatcher()
159 {
160 m_maxFD = -1;
161 }
162
163 bool wxSelectDispatcher::RegisterFD(int fd, wxFDIOHandler *handler, int flags)
164 {
165 if ( !wxMappedFDIODispatcher::RegisterFD(fd, handler, flags) )
166 return false;
167
168 if ( !m_sets.SetFD(fd, flags) )
169 return false;
170
171 if ( fd > m_maxFD )
172 m_maxFD = fd;
173
174 return true;
175 }
176
177 bool wxSelectDispatcher::ModifyFD(int fd, wxFDIOHandler *handler, int flags)
178 {
179 if ( !wxMappedFDIODispatcher::ModifyFD(fd, handler, flags) )
180 return false;
181
182 wxASSERT_MSG( fd <= m_maxFD, _T("logic error: registered fd > m_maxFD?") );
183
184 return m_sets.SetFD(fd, flags);
185 }
186
187 bool wxSelectDispatcher::UnregisterFD(int fd)
188 {
189 m_sets.ClearFD(fd);
190
191 if ( !wxMappedFDIODispatcher::UnregisterFD(fd) )
192 return false;
193
194 // remove the handler if we don't need it any more
195 if ( !m_sets.HasFD(fd) )
196 {
197 if ( fd == m_maxFD )
198 {
199 // need to find new max fd
200 m_maxFD = -1;
201 for ( wxFDIOHandlerMap::const_iterator it = m_handlers.begin();
202 it != m_handlers.end();
203 ++it )
204 {
205 if ( it->first > m_maxFD )
206 m_maxFD = it->first;
207 }
208 }
209 }
210
211 return true;
212 }
213
214 void wxSelectDispatcher::ProcessSets(const wxSelectSets& sets)
215 {
216 for ( int fd = 0; fd <= m_maxFD; fd++ )
217 {
218 if ( !sets.HasFD(fd) )
219 continue;
220
221 wxFDIOHandler * const handler = FindHandler(fd);
222 if ( !handler )
223 {
224 wxFAIL_MSG( _T("NULL handler in wxSelectDispatcher?") );
225 continue;
226 }
227
228 sets.Handle(fd, *handler);
229 }
230 }
231
232 void wxSelectDispatcher::RunLoop(int timeout)
233 {
234 struct timeval tv,
235 *ptv = NULL;
236 if ( timeout != TIMEOUT_INFINITE )
237 {
238 ptv = &tv;
239 tv.tv_sec = 0;
240 tv.tv_usec = timeout*1000;
241 }
242
243 for ( ;; )
244 {
245 wxSelectSets sets = m_sets;
246
247 wxStopWatch sw;
248 if ( ptv && timeout )
249 sw.Start(ptv->tv_usec/10);
250
251 const int ret = sets.Select(m_maxFD + 1, ptv);
252 switch ( ret )
253 {
254 case -1:
255 // continue if we were interrupted by a signal, else bail out
256 if ( errno != EINTR )
257 {
258 wxLogSysError(_("Failed to monitor I/O channels"));
259 return;
260 }
261 break;
262
263 case 0:
264 // timeout expired without anything happening
265 return;
266
267 default:
268 ProcessSets(sets);
269 }
270
271 if ( ptv )
272 {
273 timeout -= sw.Time();
274 if ( timeout <= 0 )
275 break;
276
277 ptv->tv_usec = timeout*1000;
278 }
279 }
280 }
281
282 // ----------------------------------------------------------------------------
283 // wxSelectDispatcherModule
284 // ----------------------------------------------------------------------------
285
286 class wxSelectDispatcherModule : public wxModule
287 {
288 public:
289 virtual bool OnInit() { return true; }
290 virtual void OnExit() { wxDELETE(gs_selectDispatcher); }
291
292 private:
293 DECLARE_DYNAMIC_CLASS(wxSelectDispatcherModule)
294 };
295
296 IMPLEMENT_DYNAMIC_CLASS(wxSelectDispatcherModule, wxModule)
297
298 #endif // wxUSE_SELECT_DISPATCHER