Home
Developer Resources
QNX RTOS v4
QNX RTOS v4 Knowledge Base

QNX RTOS v4 Knowledge Base

Foundry27
Foundry27
QNX RTOS v4 project
Resources

QNX RTOS v4 Knowledge Base

Title Handling IO_SELECT in QNX4
Ref. No. QNX.000009662
Category(ies) Development
Issue I need to create a resource (or iomanager) manager that responds to the POSIX select() call. I understand basic resource manager design, _IO_OPEN, _IO_CLOSE, _IO_READ, _IOWRITE, but I don't know where to get started with _IO_SELECT.



Solution I'm assuming you have the iomanager.tar.gz file from /usr/free already. It doesn't go into IO_SELECT though.

Here are some notes and a code example that may help you. It very much will be a trial and error approach in how your manager handles this call.

------------------------------------------
select() is implemented such that user programs can select() on a  variety of file descriptors which are not necessarily managed by the  same I/O managers, and by I/O managers which are not necessarily on the same CPU. The implementation tries to be as efficient as possible while still maintaining 100% compatibility with expected (UNIX) behaviour, including timeouts. In other words, the implementation of select() is not entirely obvious.

The select() library routine builds an array of (flag,fd) pairs, one for each fd to be included in the working set. The fd identifies the opened file/device. The flag field is initially zeroed, then bits are ORed in for each service which was requested on each fd in the arguments to select().  These bits are called _SEL_INPUT, _SEL_OUTPUT, and _SEL_EXCEPT).  This array is passed to each manager who modifies any entries belonging to fd's which it "owns", then passes the modified array back to the library routine which
passes the whole set to the next manager. Two passes are made. The phase is identified in the 'mode' field of the message.

The first pass is an "arm" phase (mode == _SEL_ARM) which asks each manager in
turn if any of "it's" fd's currently has anything to report. If yes, then the
manager is expected to OR in the appropriate bit (_SEL_IS_INPUT, _SEL_IS_OUTPUT, _SEL_IS_EXCEPT) for each fd which has something, and return a count of how many fd's had something to report (in the 'nfds' field). It is also expected to OR in the _SEL_POLLED bit for each fd which it "owns".  select() relies on the _SEL_POLLED bit being set to prevent it from sending duplicate messages to the same I/O manager in cases where one I/O manager "owns" more than one fd in the set (quite common).

If none of the fd's has anything available at this time, then it is expected to internally arm the appropriate fd to trigger the proxy contained in the message ('proxy' field) when something IS available to report. It should also OR in the appropriate _SEL_ARMED bit in the array of flags to allow the library routine to know who does (or doesn't) have pending triggers. The _SEL_ARMED bit should be masked off if something is available (i.e. always set it to something - 1 or 0).

The library routine only sends the "arm" phase to the next manager if every manager thus far has had nothing available (i.e. early out). If no managers have anything available, then select() will wait for the proxy or a timeout.

The next phase ("poll") occurs when one of the I/O managers triggers the proxy. select() then sends the (same) array of flags to each I/O manager after clearing the _SEL_IS_* bits. The I/O managers again set "_SEL_IS_*" bits. This time they "disarm" any internal triggers on the appropriate fd's, and again return a count. It is assumed that I/O managers always DISARM internal triggers when they receive the "poll" message.

If one of the I/O managers in the first phase (arm) reports a non-zero count, then select() skips the "arm" message for the rest of the managers and proceeds immediately to the "poll" phase for all I/O managers.

If a timeout occurs anytime, then the "poll" message is sent only to those I/O managers which ORed in _SEL_ARMED bits (allowing them to disarm internal triggers).

------------------------------------------------------------------------------

How to handle an IO_SELECT message in an IO Manager: (from some earlier tcp/ip code)

/* int select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds, struct timeval *tv) */

        define m ((struct _io_select *) db)
        define _SEL_IS_MINE 0x1000


case _IO_SELECT: {
        int nfds, rfds = 0;

        nfds = m->nfds;
        while (nfds--) {
            unsigned short flag = m->set[nfds].flag;
            short fd = m->set[nfds].fd;
            struct file *fp;

            if (flag & _SEL_POLLED)
                continue;
            if (fhandle(pid, fd, &fp) == 0) {
                struct socket *so = (struct socket *) fp->f_data;

                if (flag & _SEL_ARMED) {
                    if (kick_disarm(pid, &so->so_rcv)) {
                        flag |= _SEL_IS_INPUT;
                        ++rfds;
                    }
                    if (kick_disarm(pid, &so->so_snd)) {
                        flag |= _SEL_IS_OUTPUT;
                        ++rfds;
                    }
                }
                if (flag & _SEL_INPUT)
                    if (soreadable(so)) {
                        flag |= _SEL_IS_INPUT;
                        ++rfds;
                    }
                if (flag & _SEL_OUTPUT)
                    if (sowriteable(so)) {
                        flag |= _SEL_IS_OUTPUT;
                        ++rfds;
                    }
                if (flag & _SEL_EXCEPT)
                    if (so->so_oobmark || (so->so_state & SS_RCVATMARK)) {
                        flag |= _SEL_IS_EXCEPT;
                        ++rfds;
                    }
                m->set[nfds].flag = flag | _SEL_POLLED | _SEL_IS_MINE;
            }
        }

        if (m->mode == _SEL_ARM && rfds == 0) {
            int trigger = 0;
            nfds = m->nfds;
            while (nfds--) {
                unsigned short flag = m->set[nfds].flag;
                short fd = m->set[nfds].fd;
                struct file *fp;

                if (flag & _SEL_IS_MINE) {
                    if (fhandle(pid, fd, &fp) == 0) {
                        struct socket *so = (struct socket *) fp->f_data;

                        if (flag & _SEL_INPUT)
                            if (soreadable(so)) {
                                m->set[nfds].flag |= _SEL_IS_INPUT;
                                ++trigger;
                            } else
                                kick_arm(pid, m->proxy, &so->so_rcv);
                        if (flag & _SEL_OUTPUT)
                            if (sowriteable(so)) {
                                m->set[nfds].flag |= _SEL_IS_OUTPUT;
                                ++trigger;
                            } else
                                kick_arm(pid, m->proxy, &so->so_snd);
                        if (flag & _SEL_EXCEPT)
                            if (so->so_oobmark || (so->so_state & SS_RCVATMARK)) {
                                m->set[nfds].flag |= _SEL_IS_EXCEPT;
                                ++trigger;
                            } else
                                kick_arm(pid, m->proxy, &so->so_rcv);
                        if (!trigger)
                            m->set[nfds].flag |= _SEL_ARMED;
                    }
                }
            }
            if (trigger)
                if (m->proxy > 0) Trigger(m->proxy);
                else kill(pid, ~m->proxy);
        }

        m->type = EOK;
        nfds = m->nfds, m->nfds = rfds;
        Reply(pid, m, sizeof *m + nfds * sizeof(struct _select_set));
        break;

undef m
      }