/* 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 #include "xstd/h/os_std.h" #include "xstd/h/iostream.h" #include "xstd/h/iomanip.h" #include "xstd/Assert.h" #include "xstd/Clock.h" #include "xstd/Poll.h" #include "xstd/gadgets.h" PollFD::PollFD(int aFd) { init(aFd); } void PollFD::init(int aFd) { this->fd = aFd; clear(dirNone); } void PollFD::set(IODir dir) { if (dir == dirRead) events |= XPOLL_RD; else events |= XPOLL_WR; } void PollFD::clear(IODir dir) { if (dir == dirRead) { events &= ~XPOLL_RD; revents &= ~XPOLL_RD; } else if (dir == dirWrite) { events &= ~XPOLL_WR; revents &= ~XPOLL_WR; } else events = 0; if (!events) revents = 0; } PollFD PollFD::unidir(IODir dir) const { PollFD res = *this; res.clear((IODir)-dir); res.set(dir); return res; } /* Poll */ Poll::Poll() { } void Poll::configure(int fdLimit) { FileScanner::configure(fdLimit); thePollees.stretch(fdLimit); thePollees.count(fdLimit); theHotFDs.stretch(fdLimit); for (int fd = 0; fd < thePollees.count(); ++fd) thePollees[fd].init(fd); } FileScanReserv Poll::setFD(int fd, IODir dir, FileScanUser *u) { Assert(0 <= fd && fd < thePollees.count()); thePollees[fd].set(dir); if (fd > theMaxFD) theMaxFD = fd; return FileScanner::setFD(fd, dir, u); } void Poll::clearFD(int fd, IODir dir) { Assert(0 <= fd && fd < thePollees.count()); thePollees[fd].clear(dir); if (fd == theMaxFD) { while (theMaxFD >= 0 && !thePollees[theMaxFD].used()) { theMaxFD--; } } FileScanner::clearFD(fd, dir); } FileScanUser *Poll::readyUser(int idx, IODir dir, int &fd) { const PollFD &pfd = theHotFDs[idx]; fd = pfd.fd; // quit if fd is bad if (fd < 0) { static int bugCount = 0; if (bugCount++ % 1000 == 0) clog << here << "bug: invalid fd `" << fd << "' returned by poll(2); #" << bugCount << endl; return 0; } // quit if fd is stale if (fd > theMaxFD) return 0; // quit if poll(2) said "not ready" if (!pfd.ready(dir)) return 0; // quit if the user is no longer interested in this event // (most likely, it is a different user!) // XXX: we may notify a different (but also blocked) user if (!thePollees[fd].interested(dir) && theRegs[fd].blocked() != dir) return 0; return theRegs[fd].user(); } int Poll::sweep(Time *timeout) { theHotFDs.reset(); // copy active fds to hot array for (int fd = 0; fd <= theMaxFD; ++fd) { const PollFD &p = thePollees[fd]; const FileScanUserReg ® = theRegs[fd]; if (p.used() && reg.needsCheck()) { if (const IODir dir = (IODir)reg.blocked()) theHotFDs.append(p.unidir(dir)); else theHotFDs.append(p); } } if (!theHotFDs.count()) return 0; // XXX: should not we sleep for timeout anyway? const int res = xpoll(theHotFDs.items(), theHotFDs.count(), timeout ? timeout->msec() : -1); return res <= 0 ? res : theHotFDs.count(); }