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