#include #include #include #include "LogFile.h" #include "ThreadMonitor.h" #include "IPollSet.h" IPollSet::IPollSet() : _timeout(-1), _epollHandle(0), _eventCount(0), _events(NULL) { } IPollSet::~IPollSet() { clearFileDescriptors(); delete[](_events); } void IPollSet::createEPollHandle() { if (!_epollHandle) { // I use 5 as my hint. The man pages say that this hint isn't really // used any more. Any number greater than 0 will do. _epollHandle = epoll_create(5); if (_epollHandle < 1) { // Report error and abort. LogFile::primary().quoteAndSend("IPollSet.C epoll_create", errorString(), 0); LogFile::primary().scheduleShutdown(); } } } void IPollSet::closeEPollHandle() { if (_epollHandle > 0) { int result = close(_epollHandle); if (result) {// Report error and abort. LogFile::primary().quoteAndSend("IPollSet.C close", errorString(), 0); LogFile::primary().scheduleShutdown(); // Mark this as a bad handle so we don't try to open it again. _epollHandle = -1; } else { // Mark this so that we know we need to recreate the handle next // time. _epollHandle = 0; } } } void IPollSet::createEvents(bool forceBigger) { if (forceBigger || !_events) { // Make the size big enough to hold all possible events plus one extra. // By having one extra it makes it easy to know when we need to grow // again. If the number of events received is less than this size, we // we are okay. If it is equal to this size, then we grow. // Also, this thing has to be at least one record long or epoll_wait // will fail. Adding 1 will satisfy that precondition. int newSize = _actions.size() + 1; if (_events) { delete[](_events); TclList msg; msg<<"IPollSet.C" <<"growing" <<_eventCount <::const_iterator it = other._actions.begin(); it != other._actions.end(); it++) { epoll_event event; event.events = it->second; event.data.fd = it->first; int result = epoll_ctl(_epollHandle, EPOLL_CTL_ADD, it->first, &event); if (result) { // Report error and abort. LogFile::primary().quoteAndSend("IPollSet.C loadFromOther", errorString(), 0); LogFile::primary().scheduleShutdown(); } } } } IPollSet &IPollSet::operator =(IPollSet const &other) { clearFileDescriptors(); loadFromOther(other); return *this; } IPollSet::IPollSet(IPollSet const &other) : _timeout(-1), _epollHandle(0), _eventCount(0), _events(NULL) { loadFromOther(other); } void IPollSet::setTimeoutNone() { _timeout = -1; } void IPollSet::setTimeoutMs(int timeout) { _timeout = std::max(0, timeout); } void IPollSet::setTimeout(timeval const *timeVal) { if (timeVal) { TimeVal::Microseconds microseconds = ((TimeVal *)timeVal)->asMicroseconds(); // Always round up. Otherwise, if we have less than a millisecond to // go, it is likely that we will immediately return, and the calling // function will call us again because it is not time yet. TimeVal::Microseconds milliseconds = (microseconds + 999) / 1000; // It is possible to request a time that is longer than we can represent // and send to the poll() call. In this case we wait as long as we can, // then it is up to the caller to ask us to wait again if necessary. setTimeoutMs(std::min((TimeVal::Microseconds)std::numeric_limits< int >::max(), milliseconds)); } else { setTimeoutNone(); } } void IPollSet::addAction(int fd, int action) { if (fd < 0) return; int &actions = _actions[fd]; if (actions & action) { // Nothing to do. We are already watching this action for this fd. } else { // If there was already an action for this fd, then this is a // modification, otherwise this is an add. int operation = actions?EPOLL_CTL_MOD:EPOLL_CTL_ADD; actions |= action; epoll_event event; event.events = actions; event.data.u64 = 0; // Make valgrind happy. event.data.fd = fd; createEPollHandle(); int result = epoll_ctl(_epollHandle, operation, fd, &event); if (result) { // Report error and abort. TclList msg; msg<