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
21using namespace XmlRpc;
22
23
24XmlRpcDispatch::XmlRpcDispatch()
25{
26  _endTime = -1.0;
27  _doClear = false;
28  _inWork = false;
29}
30
31
32XmlRpcDispatch::~XmlRpcDispatch()
33{
34}
35
36// Monitor this source for the specified events and call its event handler
37// when the event occurs
38void
39XmlRpcDispatch::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.
45void
46XmlRpcDispatch::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
58void
59XmlRpcDispatch::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
72void
73XmlRpcDispatch::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.
168void
169XmlRpcDispatch::exit()
170{
171  _endTime = 0.0;   // Return from work asap
172}
173
174// Clear all sources from the monitored sources list
175void
176XmlRpcDispatch::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
190double
191XmlRpcDispatch::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