| |
| #include "XmlRpcDispatch.h" |
| #include "XmlRpcSource.h" |
| #include "XmlRpcUtil.h" |
| |
| #include <math.h> |
| |
| #if defined(_WINDOWS) |
| # include <winsock2.h> |
| |
| # define USE_FTIME |
| # if defined(_MSC_VER) |
| # define timeb _timeb |
| # define ftime _ftime |
| # endif |
| #else |
| # include <sys/time.h> |
| #endif // _WINDOWS |
| |
| |
| using namespace XmlRpc; |
| |
| |
| XmlRpcDispatch::XmlRpcDispatch() |
| { |
| _endTime = -1.0; |
| _doClear = false; |
| _inWork = false; |
| } |
| |
| |
| XmlRpcDispatch::~XmlRpcDispatch() |
| { |
| } |
| |
| // Monitor this source for the specified events and call its event handler |
| // when the event occurs |
| void |
| XmlRpcDispatch::addSource(XmlRpcSource* source, unsigned mask) |
| { |
| _sources.push_back(MonitoredSource(source, mask)); |
| } |
| |
| // Stop monitoring this source. Does not close the source. |
| void |
| XmlRpcDispatch::removeSource(XmlRpcSource* source) |
| { |
| for (SourceList::iterator it=_sources.begin(); it!=_sources.end(); ++it) |
| if (it->getSource() == source) |
| { |
| _sources.erase(it); |
| break; |
| } |
| } |
| |
| |
| // Modify the types of events to watch for on this source |
| void |
| XmlRpcDispatch::setSourceEvents(XmlRpcSource* source, unsigned eventMask) |
| { |
| for (SourceList::iterator it=_sources.begin(); it!=_sources.end(); ++it) |
| if (it->getSource() == source) |
| { |
| it->getMask() = eventMask; |
| break; |
| } |
| } |
| |
| |
| |
| // Watch current set of sources and process events |
| void |
| XmlRpcDispatch::work(double timeout) |
| { |
| // Compute end time |
| _endTime = (timeout < 0.0) ? -1.0 : (getTime() + timeout); |
| _doClear = false; |
| _inWork = true; |
| |
| // Only work while there is something to monitor |
| while (_sources.size() > 0) { |
| |
| // Construct the sets of descriptors we are interested in |
| fd_set inFd, outFd, excFd; |
| FD_ZERO(&inFd); |
| FD_ZERO(&outFd); |
| FD_ZERO(&excFd); |
| |
| int maxFd = -1; // Not used on windows |
| SourceList::iterator it; |
| for (it=_sources.begin(); it!=_sources.end(); ++it) { |
| int fd = it->getSource()->getfd(); |
| if (it->getMask() & ReadableEvent) FD_SET(fd, &inFd); |
| if (it->getMask() & WritableEvent) FD_SET(fd, &outFd); |
| if (it->getMask() & Exception) FD_SET(fd, &excFd); |
| if (it->getMask() && fd > maxFd) maxFd = fd; |
| } |
| |
| // Check for events |
| int nEvents; |
| if (timeout < 0.0) |
| nEvents = select(maxFd+1, &inFd, &outFd, &excFd, NULL); |
| else |
| { |
| struct timeval tv; |
| tv.tv_sec = (int)floor(timeout); |
| tv.tv_usec = ((int)floor(1000000.0 * (timeout-floor(timeout)))) % 1000000; |
| nEvents = select(maxFd+1, &inFd, &outFd, &excFd, &tv); |
| } |
| |
| if (nEvents < 0) |
| { |
| XmlRpcUtil::error("Error in XmlRpcDispatch::work: error in select (%d).", nEvents); |
| _inWork = false; |
| return; |
| } |
| |
| // Process events |
| for (it=_sources.begin(); it != _sources.end(); ) |
| { |
| SourceList::iterator thisIt = it++; |
| XmlRpcSource* src = thisIt->getSource(); |
| int fd = src->getfd(); |
| unsigned newMask = (unsigned) -1; |
| if (fd <= maxFd) { |
| // If you select on multiple event types this could be ambiguous |
| if (FD_ISSET(fd, &inFd)) |
| newMask &= src->handleEvent(ReadableEvent); |
| if (FD_ISSET(fd, &outFd)) |
| newMask &= src->handleEvent(WritableEvent); |
| if (FD_ISSET(fd, &excFd)) |
| newMask &= src->handleEvent(Exception); |
| |
| if ( ! newMask) { |
| _sources.erase(thisIt); // Stop monitoring this one |
| if ( ! src->getKeepOpen()) |
| src->close(); |
| } else if (newMask != (unsigned) -1) { |
| thisIt->getMask() = newMask; |
| } |
| } |
| } |
| |
| // Check whether to clear all sources |
| if (_doClear) |
| { |
| SourceList closeList = _sources; |
| _sources.clear(); |
| for (SourceList::iterator it=closeList.begin(); it!=closeList.end(); ++it) { |
| XmlRpcSource *src = it->getSource(); |
| src->close(); |
| } |
| |
| _doClear = false; |
| } |
| |
| // Check whether end time has passed |
| if (0 <= _endTime && getTime() > _endTime) |
| break; |
| } |
| |
| _inWork = false; |
| } |
| |
| |
| // Exit from work routine. Presumably this will be called from |
| // one of the source event handlers. |
| void |
| XmlRpcDispatch::exit() |
| { |
| _endTime = 0.0; // Return from work asap |
| } |
| |
| // Clear all sources from the monitored sources list |
| void |
| XmlRpcDispatch::clear() |
| { |
| if (_inWork) |
| _doClear = true; // Finish reporting current events before clearing |
| else |
| { |
| SourceList closeList = _sources; |
| _sources.clear(); |
| for (SourceList::iterator it=closeList.begin(); it!=closeList.end(); ++it) |
| it->getSource()->close(); |
| } |
| } |
| |
| |
| double |
| XmlRpcDispatch::getTime() |
| { |
| #ifdef USE_FTIME |
| struct timeb tbuff; |
| |
| ftime(&tbuff); |
| return ((double) tbuff.time + ((double)tbuff.millitm / 1000.0) + |
| ((double) tbuff.timezone * 60)); |
| #else |
| struct timeval tv; |
| struct timezone tz; |
| |
| gettimeofday(&tv, &tz); |
| return (tv.tv_sec + tv.tv_usec / 1000000.0); |
| #endif /* USE_FTIME */ |
| } |
| |
| |