示例#1
0
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
}