/* C support for scsh select call. ** Copyright (c) 1995 by Olin Shivers. */ /* To do: ** - Documentation. ** - Check Posix timeval defn. ** - What is proper include for error vals? */ #include "sysdep.h" #include #if defined(HAVE_SYS_SELECT_H) # include #endif #include #include #include #include "fdports.h" /* Accessors for Scheme I/O port internals. */ #include "cstuff.h" /* the traditional sleazy max non-function. */ #define max(a,b) (((a) > (b)) ? (a) : (b)) extern int errno; extern FILE *fdes2fstar(int fd); static void or2_fdset(fd_set *x, fd_set *y, int max_elt); static int copyback_fdvec(scheme_value portvec, fd_set *fdset); /* RVEC, WVEC, and EVEC are Scheme vectors of integer file descriptors ** and I/O ports. NSECS is an integer timeout value, or #f for infinite wait. ** Do the select() call. Move every element of we hit on to the front of ** its vector. The number of hits are returned in R_NUMRDY, W_NUMRDY, and ** E_NUMRDY. The principle return value is #f if we win, and a fixnum errno ** value if we error out. */ scheme_value scm_select(scheme_value rvec, scheme_value wvec, scheme_value evec, scheme_value nsecs, int *r_numrdy, int *w_numrdy, int *e_numrdy) { struct timeval timeout, *tptr; fd_set rset_bufrdy, wset_bufrdy, eset_bufrdy; /* Buffered port hits. */ fd_set rset_try, wset_try, eset_try; /* Real select() sets. */ int rbuf_rdy=0, wbuf_rdy=0, bufrdy; /* Set if we find buffered I/O hits. */ int max_fd = -1; /* Max fdes in the sets. */ int nelts, i; int nfound; FD_ZERO(&rset_try); FD_ZERO(&wset_try); FD_ZERO(&eset_try); FD_ZERO(&rset_bufrdy); FD_ZERO(&wset_bufrdy); FD_ZERO(&eset_bufrdy); *r_numrdy = *w_numrdy = *e_numrdy = 0; /* Scan the readvec elts. */ nelts = VECTOR_LENGTH(rvec); for(i=nelts; --i >= 0; ) { scheme_value elt = VECTOR_REF(rvec,i); int fd; if( FIXNUMP(elt) ) { /* It's an integer fdes. */ fd = EXTRACT_FIXNUM(elt); FD_SET(fd, &rset_try); } else { /* It better be a port. */ FILE *f; scheme_value data = *Port_PortData(elt); fd = EXTRACT_FIXNUM(*PortData_Fd(data)); f = fdes2fstar(fd); if( !f ) return ENTER_FIXNUM(errno); else if( *PortData_Peek(data) != SCHFALSE /* Port has a peekchar */ || !ibuf_empty(f) ) { /* Stdio buf has chars. */ FD_SET(fd, &rset_bufrdy); rbuf_rdy = 1; /* Hit. */ } else FD_SET(fd, &rset_try); /* No buffered data. */ } max_fd = max(max_fd, fd); } /* Scan the writevec elts. */ nelts = VECTOR_LENGTH(wvec); for(i=nelts; --i >= 0; ) { scheme_value elt = VECTOR_REF(wvec,i); int fd; if( FIXNUMP(elt) ) { /* It's an integer fdes. */ fd = EXTRACT_FIXNUM(elt); FD_SET(fd, &wset_try); } else { /* It better be a port. */ FILE *f; fd = EXTRACT_FIXNUM(*PortFd(elt)); f = fdes2fstar(fd); if( !f ) return ENTER_FIXNUM(errno); else if( !obuf_full(f) ) { /* I/O buf has room. */ FD_SET(fd, &wset_bufrdy); wbuf_rdy = 1; /* Hit. */ } else FD_SET(fd, &wset_try); /* No room. */ } max_fd = max(max_fd, fd); } /* Scan the exception-vec elts. */ nelts = VECTOR_LENGTH(evec); for(i=nelts; --i >= 0; ) { scheme_value elt = VECTOR_REF(evec,i); int fd; if( FIXNUMP(elt) ) { /* It's an integer fdes. */ fd = EXTRACT_FIXNUM(elt); FD_SET(fd, &rset_try); } else { /* It better be a port. */ fd = EXTRACT_FIXNUM(*PortFd(elt)); FD_SET(fd, &rset_try); } max_fd = max(max_fd, fd); } bufrdy = rbuf_rdy || wbuf_rdy; if( bufrdy ) { /* Already have some hits on buffered ports, */ timeout.tv_sec = 0; /* so we only poll the others. */ timeout.tv_usec = 0; tptr = &timeout; } else if ( FIXNUMP(nsecs) ) { timeout.tv_sec = EXTRACT_FIXNUM(nsecs); /* Wait n seconds. */ timeout.tv_usec = 0; tptr = &timeout; } else tptr = NULL; /* #f => Infinite wait. */ nfound = select1(max_fd+1, &rset_try, &wset_try, &eset_try, tptr);/*Do it.*/ /* EINTR is not an error return if we have hits on buffered ports ** to report. */ if( nfound < 0 ) if ( errno != EINTR || !bufrdy ) return ENTER_FIXNUM(errno); else { /* EINTR, but we have hits on buffered ports to report. */ FD_ZERO(&rset_try); /* This should never happen -- */ FD_ZERO(&wset_try); /* EINTR on a zero-sec select() */ FD_ZERO(&eset_try); /* -- but I'm paranoid. */ } /* OR together the buffered-io ready sets and the fd ready sets. */ if( rbuf_rdy ) or2_fdset(&rset_try, &rset_bufrdy, max_fd); if( wbuf_rdy ) or2_fdset(&wset_try, &wset_bufrdy, max_fd); *r_numrdy = copyback_fdvec(rvec, &rset_try); *w_numrdy = copyback_fdvec(wvec, &wset_try); *e_numrdy = copyback_fdvec(evec, &eset_try); return SCHFALSE; } /* PORTVEC is a vector of integer file descriptors and Scheme ports. ** Scan over the vector, and copy any elt whose file descriptor is in FDSET ** to the front of the vector. Return the number of elts thus copied. */ static int copyback_fdvec(scheme_value portvec, fd_set *fdset) { int vlen = VECTOR_LENGTH(portvec); int i, j=0; for( i = -1; ++i < vlen; ) { scheme_value elt = VECTOR_REF(portvec, i); int fd = EXTRACT_FIXNUM((FIXNUMP(elt)) ? elt : *PortFd(elt)); if( FD_ISSET(fd,fdset) ) { FD_CLR(fd,fdset); /* In case luser put elt in multiple times. */ VECTOR_REF(portvec, j) = elt; j++; } } return j; } /* x = x or y */ static void or2_fdset(fd_set *x, fd_set *y, int max_elt) { int i; for(i=max_elt+1; --i >= 0;) if( FD_ISSET(i,y) ) FD_SET(i,x); }