Dispatcher.c++
上传用户:weiyuanprp
上传日期:2020-05-20
资源大小:1169k
文件大小:17k
源码类别:

传真(Fax)编程

开发平台:

C/C++

  1. /*
  2.  * Copyright (c) 1987-1991 Stanford University
  3.  * Copyright (c) 1991-1996 Silicon Graphics, Inc.
  4.  * HylaFAX is a trademark of Silicon Graphics
  5.  *
  6.  * Permission to use, copy, modify, distribute, and sell this software and 
  7.  * its documentation for any purpose is hereby granted without fee, provided
  8.  * that (i) the above copyright notices and this permission notice appear in
  9.  * all copies of the software and related documentation, and (ii) the names of
  10.  * Stanford and Silicon Graphics may not be used in any advertising or
  11.  * publicity relating to the software without the specific, prior written
  12.  * permission of Stanford and Silicon Graphics.
  13.  * 
  14.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
  15.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
  16.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
  17.  *
  18.  * IN NO EVENT SHALL STANFORD OR SILICON GRAPHICS BE LIABLE FOR
  19.  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  20.  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  21.  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
  22.  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
  23.  * OF THIS SOFTWARE.
  24.  */
  25. // Dispatcher provides an interface to the "select" system call.
  26. #include "Sys.h" // NB: must be first
  27. #include <errno.h>
  28. #include <sys/param.h>
  29. extern "C" {
  30. #include <sys/time.h>
  31. }
  32. #include <limits.h>
  33. #include "Dispatcher.h"
  34. #include "IOHandler.h"
  35. Dispatcher* Dispatcher::_instance;
  36. /*
  37.  * Operations on timeval structures.
  38.  */
  39. const long ONE_SECOND = 1000000;
  40. timeval operator+(timeval src1, timeval src2) {
  41.     timeval sum;
  42.     sum.tv_sec = src1.tv_sec + src2.tv_sec;
  43.     sum.tv_usec = src1.tv_usec + src2.tv_usec;
  44.     if (sum.tv_usec >= ONE_SECOND) {
  45. sum.tv_usec -= ONE_SECOND;
  46. sum.tv_sec++;
  47.     } else if (sum.tv_sec >= 1 && sum.tv_usec < 0) {
  48. sum.tv_usec += ONE_SECOND;
  49. sum.tv_sec--;
  50.     }
  51.     return sum;
  52. }
  53. timeval operator-(timeval src1, timeval src2) {
  54.     timeval delta;
  55.     delta.tv_sec = src1.tv_sec - src2.tv_sec;
  56.     delta.tv_usec = src1.tv_usec - src2.tv_usec;
  57.     if (delta.tv_usec < 0) {
  58. delta.tv_usec += ONE_SECOND;
  59. delta.tv_sec--;
  60.     } else if (delta.tv_usec >= ONE_SECOND) {
  61. delta.tv_usec -= ONE_SECOND;
  62. delta.tv_sec++;
  63.     }
  64.     return delta;
  65. }
  66. bool operator>(timeval src1, timeval src2) {
  67.     if (src1.tv_sec > src2.tv_sec) {
  68. return true;
  69.     } else if (src1.tv_sec == src2.tv_sec && src1.tv_usec > src2.tv_usec) {
  70. return true;
  71.     } else {
  72. return false;
  73.     }
  74. }
  75. bool operator<(timeval src1, timeval src2) {
  76.     if (src1.tv_sec < src2.tv_sec) {
  77. return true;
  78.     } else if (src1.tv_sec == src2.tv_sec && src1.tv_usec < src2.tv_usec) {
  79. return true;
  80.     } else {
  81. return false;
  82.     }
  83. }
  84. /*
  85.  * Interface to timers.
  86.  */
  87. struct Timer {
  88.     Timer(timeval t, IOHandler* h, Timer* n);
  89.     timeval timerValue;
  90.     IOHandler* handler;
  91.     Timer* next;
  92. };
  93. class TimerQueue {
  94. public:
  95.     TimerQueue();
  96.     virtual ~TimerQueue();
  97.     bool isEmpty() const;
  98.     static timeval zeroTime();
  99.     timeval earliestTime() const;
  100.     static timeval currentTime();
  101.     void insert(timeval, IOHandler*);
  102.     void remove(IOHandler*);
  103.     void expire(timeval);
  104. private:
  105.     Timer* _first;
  106.     static timeval _zeroTime;
  107. };
  108. Timer::Timer(timeval t, IOHandler* h, Timer* n) :
  109.     timerValue(t),
  110.     handler(h),
  111.     next(n) {}
  112. timeval TimerQueue::_zeroTime;
  113. TimerQueue::TimerQueue() :
  114.     _first(NULL) {}
  115. TimerQueue::~TimerQueue() {
  116.     Timer* doomed = _first;
  117.     while (doomed != NULL) {
  118. Timer* next = doomed->next;
  119. delete doomed;
  120. doomed = next;
  121.     }
  122. }
  123. inline bool TimerQueue::isEmpty() const {
  124.     return _first == NULL;
  125. }
  126. inline timeval TimerQueue::zeroTime() {
  127.     return _zeroTime;
  128. }
  129. inline timeval TimerQueue::earliestTime() const {
  130.     return _first->timerValue;
  131. }
  132. timeval TimerQueue::currentTime() {
  133.     timeval curTime;
  134.     gettimeofday(&curTime, 0);
  135.     return curTime;
  136. }
  137. void TimerQueue::insert(timeval futureTime, IOHandler* handler) {
  138.     if (isEmpty() || futureTime < earliestTime()) {
  139. _first = new Timer(futureTime, handler, _first);
  140.     } else {
  141. Timer* before = _first;
  142. Timer* after = _first->next;
  143. while (after != NULL && futureTime > after->timerValue) {
  144.     before = after;
  145.     after = after->next;
  146. }
  147. before->next = new Timer(futureTime, handler, after);
  148.     }
  149. }
  150. void TimerQueue::remove(IOHandler* handler) {
  151.     Timer* before = NULL;
  152.     Timer* doomed = _first;
  153.     while (doomed != NULL && doomed->handler != handler) {
  154. before = doomed;
  155. doomed = doomed->next;
  156.     }
  157.     if (doomed != NULL) {
  158. if (before == NULL) {
  159.     _first = doomed->next;
  160. } else {
  161.     before->next = doomed->next;
  162. }
  163. delete doomed;
  164.     }
  165. }
  166. void TimerQueue::expire(timeval curTime) {
  167.     while (!isEmpty() && earliestTime() < curTime) {
  168. Timer* expired = _first;
  169. _first = _first->next;
  170. expired->handler->timerExpired(curTime.tv_sec, curTime.tv_usec);
  171. delete expired;
  172.     }
  173. }
  174. /*
  175.  * Interface to child process handling.
  176.  */
  177. struct Child {
  178.     Child(pid_t pid, IOHandler* h, Child* n);
  179.     pid_t pid; // process's PID
  180.     int status; // wait status
  181.     IOHandler* handler; // associated handler
  182.     Child* next;
  183. };
  184. class ChildQueue {
  185. public:
  186.     ChildQueue();
  187.     virtual ~ChildQueue();
  188.     bool isEmpty() const;
  189.     bool isReady() const;
  190.     void insert(pid_t, IOHandler*);
  191.     void remove(IOHandler*);
  192.     void notify();
  193.     void setStatus(pid_t, int status);
  194. private:
  195.     Child* _first; // queue head
  196.     bool _ready; // something is ready
  197. };
  198. Child::Child(pid_t p, IOHandler* h, Child* n)
  199. {
  200.     pid = p;
  201.     status = -1;
  202.     handler = h;
  203.     next = n;
  204. }
  205. ChildQueue::ChildQueue()
  206. {
  207.     _first = NULL;
  208.     _ready = false;
  209. }
  210. ChildQueue::~ChildQueue() {
  211.     Child* doomed = _first;
  212.     while (doomed != NULL) {
  213. Child* next = doomed->next;
  214. delete doomed;
  215. doomed = next;
  216.     }
  217. }
  218. inline bool ChildQueue::isEmpty() const { return _first == NULL; }
  219. inline bool ChildQueue::isReady() const { return _ready; }
  220. void ChildQueue::insert(pid_t p, IOHandler* handler) {
  221.     /*
  222.      * There's a race between the insertion of the pid
  223.      * into the queue and the termination of the child
  224.      * process.  If the child exits before the insertion
  225.      * occurs, then dispatcher will miss the signal.  So we
  226.      * test whether or not the pid is still running both
  227.      * before and after the insertion.
  228.      */
  229.     bool gotstatus = false;
  230.     int status;
  231.     if (waitpid(p, &status, WNOHANG) > 0) gotstatus = true;
  232.     /*
  233.      * Place the entry at the end.  This is intentional
  234.      * so that the work done in the notify method below
  235.      * functions correctly when entries are added by
  236.      * childStatus handlers.  On busy systems it may pay
  237.      * to use a doubly linked list or to otherwise sort
  238.      * the list to reduce searching, but since there
  239.      * should never be more than 2x entries where x is
  240.      * the number of ready modems on the system a singly
  241.      * linked list should be ok.
  242.      */
  243.     Child** prev = &_first;
  244.     while (*prev != NULL)
  245. prev = &(*prev)->next;
  246.     *prev = new Child(p, handler, NULL);
  247.     if (gotstatus || waitpid(p, &status, WNOHANG) > 0)
  248. setStatus(p, status);
  249. }
  250. void ChildQueue::remove(IOHandler* handler) {
  251.     Child* before = NULL;
  252.     Child* doomed = _first;
  253.     while (doomed != NULL && doomed->handler != handler) {
  254. before = doomed;
  255. doomed = doomed->next;
  256.     }
  257.     if (doomed != NULL) {
  258. if (before == NULL) {
  259.     _first = doomed->next;
  260. } else {
  261.     before->next = doomed->next;
  262. }
  263. delete doomed;
  264.     }
  265. }
  266. void ChildQueue::setStatus(pid_t p, int status) {
  267.     for (Child* c = _first; c != NULL; c = c->next)
  268. if (c->pid == p) {
  269.     c->status = status;
  270.     _ready = true;
  271.     break;
  272. }
  273. }
  274. void ChildQueue::notify() {
  275.     Child** prev = &_first;
  276.     Child* c;
  277.     while ((c = *prev) != NULL) {
  278. if (c->status != -1) {
  279.     *prev = c->next;
  280.     c->handler->childStatus(c->pid, c->status);
  281.     delete c;
  282. } else
  283.     prev = &c->next;
  284.     }
  285.     _ready = false;
  286. }
  287. Dispatcher::Dispatcher() {
  288.     _nfds = 0;
  289.     FD_ZERO(&_rmask);
  290.     FD_ZERO(&_wmask);
  291.     FD_ZERO(&_emask);
  292.     FD_ZERO(&_rmaskready);
  293.     FD_ZERO(&_wmaskready);
  294.     FD_ZERO(&_emaskready);
  295.     _max_fds = Sys::getOpenMax();
  296.     _rtable = new IOHandler*[_max_fds];
  297.     _wtable = new IOHandler*[_max_fds];
  298.     _etable = new IOHandler*[_max_fds];
  299.     _queue = new TimerQueue;
  300.     _cqueue = new ChildQueue;
  301.     for (u_int i = 0; i < _max_fds; i++) {
  302. _rtable[i] = NULL;
  303. _wtable[i] = NULL;
  304. _etable[i] = NULL;
  305.     }
  306. }
  307. Dispatcher::~Dispatcher() {
  308.     delete [] _rtable;
  309.     delete [] _wtable;
  310.     delete [] _etable;
  311.     delete _queue;
  312.     delete _cqueue;
  313. }
  314. Dispatcher& Dispatcher::instance() {
  315.     if (_instance == NULL) {
  316. _instance = new Dispatcher;
  317.     }
  318.     return *_instance;
  319. }
  320. void Dispatcher::instance(Dispatcher* d) { _instance = d; }
  321. IOHandler* Dispatcher::handler(int fd, DispatcherMask mask) const {
  322.     if ((unsigned) fd >= _max_fds) {
  323. abort();
  324.     }
  325.     IOHandler* cur = NULL;
  326.     if (mask == ReadMask) {
  327. cur = _rtable[fd];
  328.     } else if (mask == WriteMask) {
  329. cur = _wtable[fd];
  330.     } else if (mask == ExceptMask) {
  331. cur = _etable[fd];
  332.     } else {
  333. abort();
  334.     }
  335.     return cur;
  336. }
  337. void Dispatcher::link(int fd, DispatcherMask mask, IOHandler* handler) {
  338.     if ((unsigned) fd >= _max_fds) {
  339. abort();
  340.     }
  341.     attach(fd, mask, handler);
  342. }
  343. void Dispatcher::unlink(int fd) {
  344.     if ((unsigned) fd >= _max_fds) {
  345. abort();
  346.     }
  347.     detach(fd);
  348. }
  349. void Dispatcher::attach(int fd, DispatcherMask mask, IOHandler* handler) {
  350.     if (fd < 0)
  351. return;
  352.     if (mask == ReadMask) {
  353.         FD_SET(fd, &_rmask);
  354.         _rtable[fd] = handler;
  355.     } else if (mask == WriteMask) {
  356.         FD_SET(fd, &_wmask);
  357.         _wtable[fd] = handler;
  358.     } else if (mask == ExceptMask) {
  359.         FD_SET(fd, &_emask);
  360.         _etable[fd] = handler;
  361.     } else {
  362.         abort();
  363.     }
  364.     if (_nfds < (unsigned)fd+1) {
  365. _nfds = fd+1;
  366.     }
  367. }
  368. void Dispatcher::detach(int fd) {
  369.     FD_CLR(fd, &_rmask);
  370.     _rtable[fd] = NULL;
  371.     FD_CLR(fd, &_wmask);
  372.     _wtable[fd] = NULL;
  373.     FD_CLR(fd, &_emask);
  374.     _etable[fd] = NULL;
  375.     if (_nfds == (unsigned)fd+1) {
  376. while (_nfds > 0 && _rtable[_nfds-1] == NULL &&
  377.        _wtable[_nfds-1] == NULL && _etable[_nfds-1] == NULL
  378. ) {
  379.     _nfds--;
  380. }
  381.     }
  382. }
  383. void Dispatcher::startTimer(long sec, long usec, IOHandler* handler) {
  384.     timeval deltaTime;
  385.     deltaTime.tv_sec = sec;
  386.     deltaTime.tv_usec = usec;
  387.     _queue->insert(TimerQueue::currentTime() + deltaTime, handler);
  388. }
  389. void Dispatcher::stopTimer(IOHandler* handler) {
  390.     _queue->remove(handler);
  391. }
  392. void Dispatcher::startChild(pid_t pid, IOHandler* handler) {
  393.     _cqueue->insert(pid, handler);
  394. }
  395. void Dispatcher::stopChild(IOHandler* handler) {
  396.     _cqueue->remove(handler);
  397. }
  398. bool Dispatcher::setReady(int fd, DispatcherMask mask) {
  399.     if (handler(fd, mask) == NULL) {
  400.         return false;
  401.     }
  402.     if (mask == ReadMask) {
  403.         FD_SET(fd, &_rmaskready);
  404.     } else if (mask == WriteMask) {
  405.         FD_SET(fd, &_wmaskready);
  406.     } else if (mask == ExceptMask) {
  407.         FD_SET(fd, &_emaskready);
  408.     } else {
  409.         return false;
  410.     }
  411.     return true;
  412. }
  413. void Dispatcher::dispatch() {
  414.     dispatch(NULL);
  415. }
  416. bool Dispatcher::dispatch(long& sec, long& usec) {
  417.     timeval howlong;
  418.     timeval prevTime;
  419.     timeval elapsedTime;
  420.     howlong.tv_sec = sec;
  421.     howlong.tv_usec = usec;
  422.     prevTime = TimerQueue::currentTime();
  423.     bool success = dispatch(&howlong);
  424.     elapsedTime = TimerQueue::currentTime() - prevTime;
  425.     if (howlong > elapsedTime) {
  426. howlong = howlong - elapsedTime;
  427.     } else {
  428. howlong = TimerQueue::zeroTime(); /* Used all of timeout */
  429.     }
  430.     sec = howlong.tv_sec;
  431.     usec = howlong.tv_usec;
  432.     return success;
  433. }
  434. bool Dispatcher::dispatch(timeval* howlong) {
  435.     fd_set rmask;
  436.     fd_set wmask;
  437.     fd_set emask;
  438.     FD_ZERO(&rmask);
  439.     FD_ZERO(&wmask);
  440.     FD_ZERO(&emask);
  441.     int nfound = (anyReady()) ?
  442.         fillInReady(rmask, wmask, emask)
  443.         : waitFor(rmask, wmask, emask, howlong);
  444.     notify(nfound, rmask, wmask, emask);
  445.     return (nfound != 0);
  446. }
  447. bool Dispatcher::anyReady() {
  448.     if (!_cqueue->isEmpty()) {
  449.         Dispatcher::sigCLD(0); // poll for pending children
  450.         return _cqueue->isReady();
  451.     }
  452.     for (u_int i = 0; i < _nfds; i++) {
  453.         if (FD_ISSET(i, &_rmaskready) ||
  454.                 FD_ISSET(i, &_wmaskready) || FD_ISSET(i, &_emaskready)) {
  455.             return true;
  456.     }
  457.     }
  458.     return false;
  459. }
  460. int Dispatcher::fillInReady(
  461.     fd_set& rmaskret, fd_set& wmaskret, fd_set& emaskret) {
  462.     //note - this is an array copy, not a pointer assignment
  463.     rmaskret = _rmaskready;
  464.     wmaskret = _wmaskready;
  465.     emaskret = _emaskready;
  466.     FD_ZERO(&_rmaskready);
  467.     FD_ZERO(&_wmaskready);
  468.     FD_ZERO(&_emaskready);
  469.     int n = 0;
  470.     for (u_int i = 0; i < _nfds; i++) {
  471.         if (FD_ISSET(i, &rmaskret)) n++;
  472.         if (FD_ISSET(i, &wmaskret)) n++;
  473.         if (FD_ISSET(i, &emaskret)) n++;
  474.     }
  475.     return n;
  476. }
  477. void Dispatcher::sigCLD(int)
  478. {
  479.     int old_errno = errno;
  480.     pid_t pid;
  481.     int status;
  482.     while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
  483. Dispatcher::instance()._cqueue->setStatus(pid, status);
  484.     errno = old_errno;
  485. }
  486. #ifndef SA_INTERRUPT
  487. #define SA_INTERRUPT 0
  488. #endif
  489. int Dispatcher::waitFor(
  490.     fd_set& rmaskret, fd_set& wmaskret, fd_set& emaskret, timeval* howlong
  491. ) {
  492.     int nfound = 0;
  493. #if defined(SA_NOCLDSTOP) // POSIX
  494.     static struct sigaction sa, osa;
  495. #elif defined(SV_INTERRUPT) // BSD-style
  496.     static struct sigvec sv, osv;
  497. #else // System V-style
  498.     void (*osig)();
  499. #endif
  500.     if (!_cqueue->isEmpty()) {
  501. #if defined(SA_NOCLDSTOP) // POSIX
  502. sa.sa_handler = fxSIGACTIONHANDLER(&Dispatcher::sigCLD);
  503. sa.sa_flags = SA_INTERRUPT;
  504. sigaction(SIGCHLD, &sa, &osa);
  505. #elif defined(SV_INTERRUPT) // BSD-style
  506. sv.sv_handler = fxSIGVECHANDLER(&Dispatcher::sigCLD);
  507. sv.sv_flags = SV_INTERRUPT;
  508. sigvec(SIGCHLD, &sv, &osv);
  509. #else // System V-style
  510. osig = (void (*)())signal(SIGCLD, fxSIGHANDLER(&Dispatcher::sigCLD));
  511. #endif
  512.     }
  513.     /*
  514.      * If SIGCLD is pending then it may be delivered on
  515.      * exiting from the kernel after the above sig* call;
  516.      * if so then we don't want to block in the select.
  517.      */
  518.     if (!_cqueue->isReady()) {
  519. do {
  520.     //note - this is an array copy, not a pointer assignment
  521.     rmaskret = _rmask;
  522.     wmaskret = _wmask;
  523.     emaskret = _emask;
  524.     howlong = calculateTimeout(howlong);
  525. #if CONFIG_BADSELECTPROTO
  526.     nfound = select(_nfds,
  527. (int*) &rmaskret, (int*) &wmaskret, (int*) &emaskret, howlong);
  528. #else
  529.     nfound = select(_nfds, &rmaskret, &wmaskret, &emaskret, howlong);
  530. #endif
  531.     howlong = calculateTimeout(howlong);
  532. } while (nfound < 0 && !handleError());
  533.     }
  534.     if (!_cqueue->isEmpty()) {
  535. #if defined(SA_NOCLDSTOP) // POSIX
  536. sigaction(SIGCHLD, &osa, (struct sigaction*) 0);
  537. #elif defined(SV_INTERRUPT) // BSD-style
  538. sigvec(SIGCHLD, &osv, (struct sigvec*) 0);
  539. #else // System V-style
  540. (void) signal(SIGCLD, fxSIGHANDLER(osig));
  541. #endif
  542.     }
  543.     return nfound; // timed out or input available
  544. }
  545. void Dispatcher::notify(int nfound,
  546.         fd_set& rmaskret, fd_set& wmaskret, fd_set& emaskret) {
  547.     for (u_int i = 0; i < _nfds && nfound > 0; i++) {
  548.         if (FD_ISSET(i, &rmaskret)) {
  549.             if (_rtable[i]) {
  550.                 int status = _rtable[i]->inputReady(i);
  551.                 if (status < 0) {
  552.                         detach(i);
  553.                     } else if (status > 0) {
  554.                         FD_SET(i, &_rmaskready);
  555.                 }
  556.             }
  557.             nfound--;
  558.         }
  559.         if (FD_ISSET(i, &wmaskret)) {
  560.             if (_wtable[i]) {
  561.                 int status = _wtable[i]->outputReady(i);
  562.                 if (status < 0) {
  563.                     detach(i);
  564.                 } else if (status > 0) {
  565.                     FD_SET(i, &_wmaskready);
  566.                 }
  567.             }
  568.             nfound--;
  569.         }
  570.         if (FD_ISSET(i, &emaskret)) {
  571.             if (_etable[i]) {
  572.                 int status = _etable[i]->exceptionRaised(i);
  573.                 if (status < 0) {
  574.                     detach(i);
  575.                 } else if (status > 0) {
  576.                     FD_SET(i, &_emaskready);
  577.                 }
  578.             }
  579.             nfound--;
  580.         }
  581.     }
  582.     if (!_queue->isEmpty()) {
  583.         _queue->expire(TimerQueue::currentTime());
  584.     }
  585.     if (_cqueue->isReady()) {
  586.         _cqueue->notify();
  587.     }
  588. }
  589. timeval* Dispatcher::calculateTimeout(timeval* howlong) const {
  590.     static timeval timeout;
  591.     if (!_queue->isEmpty()) {
  592. timeval curTime;
  593. curTime = TimerQueue::currentTime();
  594. if (_queue->earliestTime() > curTime) {
  595.     timeout = _queue->earliestTime() - curTime;
  596.     if (howlong == NULL || *howlong > timeout) {
  597. howlong = &timeout;
  598.     }
  599. } else {
  600.     timeout = TimerQueue::zeroTime();
  601.     howlong = &timeout;
  602. }
  603.     }
  604.     return howlong;
  605. }
  606. extern void fxFatal(const char* fmt, ...);
  607. bool Dispatcher::handleError() {
  608.     switch (errno) {
  609.     case EBADF:
  610. checkConnections();
  611. break;
  612.     case EINTR:
  613. if (_cqueue->isReady())
  614.     return true;
  615. break;
  616.     default:
  617. fxFatal("Dispatcher: select: %s", strerror(errno));
  618. /*NOTREACHED*/
  619.     }
  620.     return false; // retry select
  621. }
  622. void Dispatcher::checkConnections() {
  623.     fd_set rmask;
  624.     FD_ZERO(&rmask);
  625.     timeval poll = TimerQueue::zeroTime();
  626.     for (u_int fd = 0; fd < _nfds; fd++) {
  627.         if (_rtable[fd] != NULL) {
  628.             FD_SET(fd, &rmask);
  629. #if CONFIG_BADSELECTPROTO
  630.             if (select(fd+1, (int*)&rmask, NULL, NULL, &poll) < 0) {
  631. #else
  632.             if (select(fd+1, &rmask, NULL, NULL, &poll) < 0) {
  633. #endif
  634.              detach(fd);
  635.             }
  636.             FD_CLR(fd, &rmask);
  637.         }
  638.     }
  639. }