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