/* Web Polygraph http://www.web-polygraph.org/ * (C) 2003-2006 The Measurement Factory * Licensed under the Apache License, Version 2.0 */ #include "xstd/xstd.h" #include "xstd/h/os_std.h" #include "xstd/Assert.h" #include "xstd/Select.h" #include "xstd/gadgets.h" FD_Set::FD_Set(): theMaxFD(-1), theResCount(0) { FD_ZERO(&theSet); FD_ZERO(&theReadySet); } FD_Set::~FD_Set() { } void FD_Set::fdLimit(int) { } void FD_Set::setFD(int fd, FileScanUser *u) { Assert(fd >= 0 && u); FD_SET(fd, &theSet); if (fd > theMaxFD) theMaxFD = fd; theResCount++; } void FD_Set::clearFD(int fd) { Assert(fd >= 0); Assert(fd <= theMaxFD); FD_CLR(fd, &theSet); FD_CLR(fd, &theReadySet); // poll() cannot do that if (fd == theMaxFD) { while (theMaxFD >= 0 && !FD_ISSET(theMaxFD, &theSet)) { theMaxFD--; } } theResCount--; } /* Select */ Select::Select() { } void Select::configure(int fdLimit) { FileScanner::configure(fdLimit); theSets[dirRead].fdLimit(fdLimit); theSets[dirWrite].fdLimit(fdLimit); } FileScanReserv Select::setFD(int fd, IODir dir, FileScanUser *u) { theSets[dir].setFD(fd, u); if (fd > theMaxFD) theMaxFD = fd; return FileScanner::setFD(fd, dir, u); } void Select::clearFD(int fd, IODir dir) { theSets[dir].clearFD(fd); if (fd == theMaxFD) theMaxFD = Max(theSets[dirRead].maxFD(), theSets[dirWrite].maxFD()); FileScanner::clearFD(fd, dir); } bool Select::interestedUser(int idx, IODir dir) const { return theSets[dir].isSet(idx) && theRegs[idx].blocked() != -dir; } FileScanUser *Select::readyUser(int idx, IODir dir, int &fd) { fd = idx; return theSets[dir].isReady(fd) ? theRegs[fd].user() : 0; } fd_set *Select::prepReadySet(IODir dir) { if (fd_set *res = theSets[dir].prepReadySet()) { for (int fd = 0; fd <= theMaxFD; ++fd) { if (!interestedUser(fd, dir)) FD_CLR(fd, res); } return res; } return 0; } int Select::sweep(Time *timeout) { // have to check all fds, cannot choose like poll() does const int hotCount = theMaxFD+1; fd_set *rdSet = prepReadySet(dirRead); fd_set *wrSet = prepReadySet(dirWrite); fd_set *excSet = 0; # if defined(WIN32) // MS Windows reports connect(2) failures via exception set // collect exceptions and later "backport" to wrSet to fake // Unix-like behavior if (wrSet) { static fd_set es; es = *wrSet; excSet = &es; } else if (!rdSet) { // Windows select(2) fails if no FDs are checked for // we must emulate sleep instead of calling select(2) if (timeout) sleep(timeout->sec()); return 0; } # endif const int res = ::select(hotCount, rdSet, wrSet, excSet, timeout); # if defined(WIN32) if (res > 0 && excSet && wrSet) { for (int fd = 0; fd < hotCount; ++fd) { if (FD_ISSET(fd, excSet)) FD_SET(fd, wrSet); } } # endif return res <= 0 ? res : hotCount; }