void d4Thread::_select_io (const TimeSpec* due) { assert( _msgpipe_rfd >= 0); assert(sched.msgpipe_wfd >= 0); for (;;) { /****************************** file descriptors sets ******************************/ _fdset[IO_read ].zero(); _fdset[IO_write].zero(); _fdset[IO_error].zero(); for (FdModeSid_Map::iterator i = _fms_map.begin(); i != _fms_map.end(); ++i) { EvIO* evio = (*i).second; if (evio->active()) { _fdset[evio->mode()].add_fd(evio->fd()); } } // highest descriptor including '_msgpipe_rfd' which... const int max_fd = max(_fdset[IO_read ].max_fd, max(_fdset[IO_write].max_fd, max(_fdset[IO_error].max_fd, _msgpipe_rfd))); // will be add_fd()'ed after assert(s) #if COEXX_SELECT_USE_FD_SET // check if _msgpipe_rfd not clobbered assert(0 == FD_ISSET( _msgpipe_rfd, &_fdset[IO_read ].lval)); assert(0 == FD_ISSET( _msgpipe_rfd, &_fdset[IO_write].lval)); assert(0 == FD_ISSET( _msgpipe_rfd, &_fdset[IO_error].lval)); // check if sched.msgpipe_wfd not clobbered assert(0 == FD_ISSET(sched.msgpipe_wfd, &_fdset[IO_read ].lval)); assert(0 == FD_ISSET(sched.msgpipe_wfd, &_fdset[IO_write].lval)); assert(0 == FD_ISSET(sched.msgpipe_wfd, &_fdset[IO_error].lval)); #else // align all lvec(s) to the longest, enlarged to assumption made in checks { int max_fd_and_pipes = max(max_fd, // _msgpipe_rfd included in max_fd sched.msgpipe_wfd); _fdset[IO_read ].fit_fd(max_fd_and_pipes); _fdset[IO_write].fit_fd(max_fd_and_pipes); _fdset[IO_error].fit_fd(max_fd_and_pipes); } // check if _msgpipe_rfd not clobbered { int word = _msgpipe_rfd / NFDBITS; fd_mask mask = 1UL << (_msgpipe_rfd % NFDBITS); assert(0 == (_fdset[IO_read ].lvec[word] & mask)); assert(0 == (_fdset[IO_write].lvec[word] & mask)); assert(0 == (_fdset[IO_error].lvec[word] & mask)); } // check if sched.msgpipe_wfd not clobbered { int word = sched.msgpipe_wfd / NFDBITS; fd_mask mask = 1UL << (sched.msgpipe_wfd % NFDBITS); assert(0 == (_fdset[IO_read ].lvec[word] & mask)); assert(0 == (_fdset[IO_write].lvec[word] & mask)); assert(0 == (_fdset[IO_error].lvec[word] & mask)); } #endif // finally add '_msgpipe_rfd' _fdset[IO_read].add_fd(_msgpipe_rfd); /****************************** timeout ******************************/ struct timeval tmo; if (NULL != due) { TimeSpec delta = *due - get_current_time(); if (delta > TimeSpec::ZERO()) { delta += TimeSpec(0, 999); // ns(s) to us(s) round up tmo.tv_sec = delta.tv_sec; tmo.tv_usec = delta.tv_nsec / 1000; } else { // polling tmo.tv_sec = 0; tmo.tv_usec = 0; } } /****************************** select ******************************/ int result = select(max_fd + 1, _fdset[IO_read ].sel_set(), _fdset[IO_write].sel_set(), _fdset[IO_error].sel_set(), (due ? &tmo : NULL)); if (result == -1) { if (errno == EINTR) { /* * From POSIX `select' page * (http://www.opengroup.org/onlinepubs/9699919799/): * On failure, the objects pointed to by the readfds, * writefds, and errorfds arguments shall not be modified. * If the timeout interval expires without the specified * condition being true for any of the specified file * descriptors, the objects pointed to by the readfds, * writefds, and errorfds arguments shall have all bits set * to 0. * * From Linux `select' manpage: * On error, -1 is returned, and errno is set appropriately; * the sets and timeout become undefined, so do not rely * on their contents after an error. * **/ continue; // goto (file descriptors sets) } // // diagnostics on EBADF and possibly EINVAL // int last_errno = errno; { char errbuf[1024]; // _msgpipe_rfd assert(_msgpipe_rfd >= 0); _fdset[IO_read].zero(); _fdset[IO_read].add_fd(_msgpipe_rfd); struct timeval no_wait = {0, 0}; if (select(_msgpipe_rfd + 1, _fdset[IO_read].sel_set(), NULL, NULL, &no_wait) == -1) { sprintf(errbuf, "select(internal-read-pipe-FD=%d)", _msgpipe_rfd); perror(errbuf); } // user's fd(s) for (FdModeSid_Map::iterator i = _fms_map.begin(); i != _fms_map.end(); ++i) { EvIO* evio = (*i).second; if (evio->active()) { int fd = evio->fd(); assert(fd >= 0); IO_Mode mode = evio->mode(); assert(mode >= 0 && mode < 3); fd_set* selset[3] = {NULL, NULL, NULL}; FdSet* oneset = &_fdset[mode]; oneset->zero(); oneset->add_fd(fd); selset[mode] = oneset->sel_set(); struct timeval no_wait = {0, 0}; if (select(fd + 1, selset[0], selset[1], selset[2], &no_wait) == -1) { sprintf(errbuf, "select(FD=%d, MODE=%d, EV=%s)", fd, int(mode), evio->name().c_str()); perror(errbuf); } } } } errno = last_errno; perror("select"); abort(); } /****************************** update (guarded) ******************************/ // --@@-- Mutex::Guard guard(sched.mutex); _pqueue_pending_events(); _timestamp = get_current_time(); if (NULL != due && _timestamp >= *due) { _pqueue_expired_alarms(); } if (result > 0) { // read a byte from the notification pipe if (_fdset[IO_read].fd_isset(_msgpipe_rfd)) { char buf[16]; ssize_t nbytes = read(_msgpipe_rfd, buf, 8); // try 8, expected 1 if (nbytes == -1) { if (errno == EINTR || errno == EAGAIN) { continue; // goto (file descriptors sets) } perror("read(pipe)"); abort(); } else { if (nbytes != 1) { for (int i = 0; i < nbytes; ++i) buf[i] = buf[i] == '@' ? '@' : '.'; (cerr << "read(pipe): " << nbytes << " bytes \"").write(buf, nbytes) << "\" read!" << endl; abort(); } sched.msgpipe_flag = 0; // pipe emptied } -- result; } // // _pqueue_io_events // if (result > 0) { if (sched.msgpipe_flag) { /* * `msgpipe' might not luck into the select altogether with other * descriptors, but definitely should do the next time. * If the pipe has been signalled I increment the flag in order to * isolate a potential application bug which emptying the * pipe breaks its signalling capability. */ sched.msgpipe_flag ++; } if (sched.msgpipe_flag > 2) { /* * If tests confirm this never occurs, or if so, only due to a bug * as stated in the comment above, remove the following * assert completely or raise an exception. */ assert(sched.msgpipe_flag <= 2); sched.msgpipe_flag = 0; } for (FdModeSid_Map::iterator i = _fms_map.begin(); i != _fms_map.end(); ++i) { EvIO* evio = (*i).second; if (evio->active() && _fdset[evio->mode()].fd_isset(evio->fd())) { _pqueue.put_tail(evio); } } } } // // check the predicate // if (! _pqueue.empty()) { sched.state = Sched::BUSY; return; } } ///// for (;;) -- file descriptors sets }