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