handle 0 timeout in RunLoop() correctly
[wxWidgets.git] / src / common / selectdispatcher.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/socketevtdispatch.cpp
3 // Purpose: implements dispatcher for select() call
4 // Author: Lukasz Michalski
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 #include <sys/time.h>
33 #include <unistd.h>
34
35 #ifdef HAVE_SYS_SELECT_H
36 # include <sys/select.h>
37 #endif
38
39 #define wxSelectDispatcher_Trace wxT("selectdispatcher")
40
41 // ============================================================================
42 // implementation
43 // ============================================================================
44
45 // ----------------------------------------------------------------------------
46 // wxSelectDispatcher
47 // ----------------------------------------------------------------------------
48
49 wxSelectDispatcher* wxSelectDispatcher::ms_instance = NULL;
50
51 /* static */
52 wxSelectDispatcher& wxSelectDispatcher::Get()
53 {
54 if ( !ms_instance )
55 ms_instance = new wxSelectDispatcher;
56 return *ms_instance;
57 }
58
59 void
60 wxSelectDispatcher::RegisterFD(int fd, wxFDIOHandler* handler, int flags)
61 {
62 if ((flags & wxSelectInput) == wxSelectInput)
63 {
64 wxFD_SET(fd, &m_readset);
65 wxLogTrace(wxSelectDispatcher_Trace,wxT("Registered fd %d for input events"),fd);
66 };
67
68 if ((flags & wxSelectOutput) == wxSelectOutput)
69 {
70 wxFD_SET(fd, &m_writeset);
71 wxLogTrace(wxSelectDispatcher_Trace,wxT("Registered fd %d for output events"),fd);
72 }
73
74 if ((flags & wxSelectException) == wxSelectException)
75 {
76 wxFD_SET(fd, &m_exeptset);
77 wxLogTrace(wxSelectDispatcher_Trace,wxT("Registered fd %d for exception events"),fd);
78 };
79
80 m_handlers[fd] = handler;
81 if (fd > m_maxFD)
82 m_maxFD = fd;
83 }
84
85 wxFDIOHandler*
86 wxSelectDispatcher::UnregisterFD(int fd, int flags)
87 {
88 // GSocket likes to unregister -1 descriptor
89 if (fd == -1)
90 return NULL;
91
92 if ((flags & wxSelectInput) == wxSelectInput)
93 {
94 wxLogTrace(wxSelectDispatcher_Trace,wxT("Unregistered fd %d from input events"),fd);
95 wxFD_CLR(fd, &m_readset);
96 }
97
98 if ((flags & wxSelectOutput) == wxSelectOutput)
99 {
100 wxLogTrace(wxSelectDispatcher_Trace,wxT("Unregistered fd %d from output events"),fd);
101 wxFD_CLR(fd, &m_writeset);
102 }
103
104 if ((flags & wxSelectException) == wxSelectException)
105 {
106 wxLogTrace(wxSelectDispatcher_Trace,wxT("Unregistered fd %d from exeption events"),fd);
107 wxFD_CLR(fd, &m_exeptset);
108 };
109
110 wxFDIOHandler* ret = NULL;
111 wxFDIOHandlerMap::const_iterator it = m_handlers.find(fd);
112 if (it != m_handlers.end())
113 {
114 ret = it->second;
115 if (!wxFD_ISSET(fd,&m_readset) && !wxFD_ISSET(fd,&m_writeset) && !wxFD_ISSET(fd,&m_exeptset))
116 {
117 m_handlers.erase(it);
118 if ( m_handlers.empty() )
119 m_maxFD = 0;
120 };
121 };
122 return ret;
123 }
124
125 void wxSelectDispatcher::ProcessSets(fd_set* readset, fd_set* writeset, fd_set* exeptset, int max_fd)
126 {
127 // it is safe to remove handler from onXXX methods,
128 // if you unregister descriptor first.
129 wxFDIOHandlerMap::const_iterator it = m_handlers.begin();
130 for ( int i = 0; i < max_fd; i++ )
131 {
132 wxFDIOHandler* handler = NULL;
133 if (wxFD_ISSET(i, readset))
134 {
135 wxLogTrace(wxSelectDispatcher_Trace,wxT("Got read event on fd %d"),i);
136 handler = FindHandler(i);
137 if (handler != NULL && wxFD_ISSET(i,&m_readset))
138 handler->OnReadWaiting(i);
139 else
140 {
141 wxLogError(wxT("Lost fd in read fdset: %d, removing"),i);
142 wxFD_CLR(i,&m_readset);
143 };
144 };
145
146 if (wxFD_ISSET(i, writeset))
147 {
148 wxLogTrace(wxSelectDispatcher_Trace,wxT("Got write event on fd %d"),i);
149 if (handler == NULL)
150 handler = FindHandler(i);
151 if (handler != NULL && wxFD_ISSET(i,&m_writeset))
152 handler->OnWriteWaiting(i);
153 else
154 {
155 wxLogError(wxT("Lost fd in write fdset: %d, removing"),i);
156 wxFD_CLR(i,&m_writeset);
157 };
158 };
159
160 if (wxFD_ISSET(i, exeptset))
161 {
162 wxLogTrace(wxSelectDispatcher_Trace,wxT("Got exception event on fd %d"),i);
163 if (handler == NULL)
164 handler = FindHandler(i);
165 if (handler != NULL && wxFD_ISSET(i,&m_writeset))
166 handler->OnExceptionWaiting(i);
167 else
168 {
169 wxLogError(wxT("Lost fd in exept fdset: %d, removing"),i);
170 wxFD_CLR(i,&m_exeptset);
171 };
172 };
173 };
174 }
175
176 wxFDIOHandler* wxSelectDispatcher::FindHandler(int fd)
177 {
178 wxFDIOHandlerMap::const_iterator it = m_handlers.find(fd);
179 if (it != m_handlers.end())
180 return it->second;
181 return NULL;
182 };
183
184 void wxSelectDispatcher::RunLoop(int timeout)
185 {
186 struct timeval tv, *ptv = NULL;
187 if ( timeout != wxSELECT_TIMEOUT_INFINITE )
188 {
189 ptv = &tv;
190 tv.tv_sec = 0;
191 tv.tv_usec = timeout*10;
192 };
193
194 int ret;
195 do
196 {
197 fd_set readset = m_readset;
198 fd_set writeset = m_writeset;
199 fd_set exeptset = m_exeptset;
200 wxStopWatch sw;
201 if ( ptv && timeout )
202 sw.Start(ptv->tv_usec/10);
203 ret = select(m_maxFD+1, &readset, &writeset, &exeptset, ptv);
204 switch ( ret )
205 {
206 // TODO: handle unix signals here
207 case -1:
208 if ( !timeout )
209 {
210 // it doesn't make sense to remain here
211 return;
212 }
213
214 if ( ptv )
215 {
216 ptv->tv_sec = 0;
217 ptv->tv_usec = timeout - sw.Time()*10;
218 }
219 break;
220
221 // timeout
222 case 0:
223 break;
224
225 default:
226 ProcessSets(&readset, &writeset, &exeptset, m_maxFD+1);
227 };
228 } while (ret != 0);
229 }
230
231 // ----------------------------------------------------------------------------
232 // wxSelectDispatcherModule
233 // ----------------------------------------------------------------------------
234
235 class wxSelectDispatcherModule: public wxModule
236 {
237 public:
238 bool OnInit() { wxLog::AddTraceMask(wxSelectDispatcher_Trace); return true; }
239 void OnExit() { wxDELETE(wxSelectDispatcher::ms_instance); }
240
241 private:
242 DECLARE_DYNAMIC_CLASS(wxSelectDispatcherModule)
243 };
244
245 IMPLEMENT_DYNAMIC_CLASS(wxSelectDispatcherModule, wxModule)
246