1 
2 #include "XmlRpcDispatch.h"
3 #include "XmlRpcSource.h"
4 #include "XmlRpcUtil.h"
5 
6 #include <math.h>
7 
8 #if defined(_WINDOWS)
9 # include <winsock2.h>
10 
11 # define USE_FTIME
12 # if defined(_MSC_VER)
13 #  define timeb _timeb
14 #  define ftime _ftime
15 # endif
16 #else
17 # include <sys/time.h>
18 #endif  // _WINDOWS
19 
20 
21 using namespace XmlRpc;
22 
23 
XmlRpcDispatch()24 XmlRpcDispatch::XmlRpcDispatch()
25 {
26   _endTime = -1.0;
27   _doClear = false;
28   _inWork = false;
29 }
30 
31 
~XmlRpcDispatch()32 XmlRpcDispatch::~XmlRpcDispatch()
33 {
34 }
35 
36 // Monitor this source for the specified events and call its event handler
37 // when the event occurs
38 void
addSource(XmlRpcSource * source,unsigned mask)39 XmlRpcDispatch::addSource(XmlRpcSource* source, unsigned mask)
40 {
41   _sources.push_back(MonitoredSource(source, mask));
42 }
43 
44 // Stop monitoring this source. Does not close the source.
45 void
removeSource(XmlRpcSource * source)46 XmlRpcDispatch::removeSource(XmlRpcSource* source)
47 {
48   for (SourceList::iterator it=_sources.begin(); it!=_sources.end(); ++it)
49     if (it->getSource() == source)
50     {
51       _sources.erase(it);
52       break;
53     }
54 }
55 
56 
57 // Modify the types of events to watch for on this source
58 void
setSourceEvents(XmlRpcSource * source,unsigned eventMask)59 XmlRpcDispatch::setSourceEvents(XmlRpcSource* source, unsigned eventMask)
60 {
61   for (SourceList::iterator it=_sources.begin(); it!=_sources.end(); ++it)
62     if (it->getSource() == source)
63     {
64       it->getMask() = eventMask;
65       break;
66     }
67 }
68 
69 
70 
71 // Watch current set of sources and process events
72 void
work(double timeout)73 XmlRpcDispatch::work(double timeout)
74 {
75   // Compute end time
76   _endTime = (timeout < 0.0) ? -1.0 : (getTime() + timeout);
77   _doClear = false;
78   _inWork = true;
79 
80   // Only work while there is something to monitor
81   while (_sources.size() > 0) {
82 
83     // Construct the sets of descriptors we are interested in
84     fd_set inFd, outFd, excFd;
85 	  FD_ZERO(&inFd);
86 	  FD_ZERO(&outFd);
87 	  FD_ZERO(&excFd);
88 
89     int maxFd = -1;     // Not used on windows
90     SourceList::iterator it;
91     for (it=_sources.begin(); it!=_sources.end(); ++it) {
92       int fd = it->getSource()->getfd();
93       if (it->getMask() & ReadableEvent) FD_SET(fd, &inFd);
94       if (it->getMask() & WritableEvent) FD_SET(fd, &outFd);
95       if (it->getMask() & Exception)     FD_SET(fd, &excFd);
96       if (it->getMask() && fd > maxFd)   maxFd = fd;
97     }
98 
99     // Check for events
100     int nEvents;
101     if (timeout < 0.0)
102       nEvents = select(maxFd+1, &inFd, &outFd, &excFd, NULL);
103     else
104     {
105       struct timeval tv;
106       tv.tv_sec = (int)floor(timeout);
107       tv.tv_usec = ((int)floor(1000000.0 * (timeout-floor(timeout)))) % 1000000;
108       nEvents = select(maxFd+1, &inFd, &outFd, &excFd, &tv);
109     }
110 
111     if (nEvents < 0)
112     {
113       XmlRpcUtil::error("Error in XmlRpcDispatch::work: error in select (%d).", nEvents);
114       _inWork = false;
115       return;
116     }
117 
118     // Process events
119     for (it=_sources.begin(); it != _sources.end(); )
120     {
121       SourceList::iterator thisIt = it++;
122       XmlRpcSource* src = thisIt->getSource();
123       int fd = src->getfd();
124       unsigned newMask = (unsigned) -1;
125       if (fd <= maxFd) {
126         // If you select on multiple event types this could be ambiguous
127         if (FD_ISSET(fd, &inFd))
128           newMask &= src->handleEvent(ReadableEvent);
129         if (FD_ISSET(fd, &outFd))
130           newMask &= src->handleEvent(WritableEvent);
131         if (FD_ISSET(fd, &excFd))
132           newMask &= src->handleEvent(Exception);
133 
134         if ( ! newMask) {
135           _sources.erase(thisIt);  // Stop monitoring this one
136           if ( ! src->getKeepOpen())
137             src->close();
138         } else if (newMask != (unsigned) -1) {
139           thisIt->getMask() = newMask;
140         }
141       }
142     }
143 
144     // Check whether to clear all sources
145     if (_doClear)
146     {
147       SourceList closeList = _sources;
148       _sources.clear();
149       for (SourceList::iterator it=closeList.begin(); it!=closeList.end(); ++it) {
150 	XmlRpcSource *src = it->getSource();
151         src->close();
152       }
153 
154       _doClear = false;
155     }
156 
157     // Check whether end time has passed
158     if (0 <= _endTime && getTime() > _endTime)
159       break;
160   }
161 
162   _inWork = false;
163 }
164 
165 
166 // Exit from work routine. Presumably this will be called from
167 // one of the source event handlers.
168 void
exit()169 XmlRpcDispatch::exit()
170 {
171   _endTime = 0.0;   // Return from work asap
172 }
173 
174 // Clear all sources from the monitored sources list
175 void
clear()176 XmlRpcDispatch::clear()
177 {
178   if (_inWork)
179     _doClear = true;  // Finish reporting current events before clearing
180   else
181   {
182     SourceList closeList = _sources;
183     _sources.clear();
184     for (SourceList::iterator it=closeList.begin(); it!=closeList.end(); ++it)
185       it->getSource()->close();
186   }
187 }
188 
189 
190 double
getTime()191 XmlRpcDispatch::getTime()
192 {
193 #ifdef USE_FTIME
194   struct timeb	tbuff;
195 
196   ftime(&tbuff);
197   return ((double) tbuff.time + ((double)tbuff.millitm / 1000.0) +
198 	  ((double) tbuff.timezone * 60));
199 #else
200   struct timeval	tv;
201   struct timezone	tz;
202 
203   gettimeofday(&tv, &tz);
204   return (tv.tv_sec + tv.tv_usec / 1000000.0);
205 #endif /* USE_FTIME */
206 }
207 
208 
209