/* C support for scsh select call. ** Copyright (c) 1995 by Olin Shivers. */ #include "sysdep.h" #include #if defined(HAVE_SYS_SELECT_H) # include #endif #include #include #include #include "cstuff.h" #include "fdports.h" /* Accessors for Scheme I/O port internals. */ #include "fdports1.h" /* Import fdes2fstar(). */ #include "machine/stdio_dep.h" /* Import stdio buf-peeking ops. */ /* Make sure our exports match up w/the implementation: */ #include "select1.h" /* the traditional sleazy max non-function. */ #define max(a,b) (((a) > (b)) ? (a) : (b)) 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, ** I/O ports, and #f's. NSECS is an integer timeout value, or #f for ** infinite wait. Do the select() call, returning result fd_sets in the ** passed pointers. Return 0 for OK, otherwise error is in errno. */ int do_select(scheme_value rvec, scheme_value wvec, scheme_value evec, scheme_value nsecs, fd_set *rset_ans, fd_set *wset_ans, fd_set *eset_ans) { struct timeval timeout, *tptr; fd_set rset_bufrdy, wset_bufrdy, eset_bufrdy; /* Buffered port hits. */ 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_ans); FD_ZERO(wset_ans); FD_ZERO(eset_ans); FD_ZERO(&rset_bufrdy); FD_ZERO(&wset_bufrdy); FD_ZERO(&eset_bufrdy); /* 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_ans); } else if( elt != SCHFALSE ) { /* 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 -1; 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_ans); /* 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_ans); } else if( elt != SCHFALSE ) { /* It better be a port. */ FILE *f; fd = EXTRACT_FIXNUM(*PortFd(elt)); f = fdes2fstar(fd); if( !f ) return -1; else if( !obuf_full(f) ) { /* I/O buf has room. */ FD_SET(fd, &wset_bufrdy); wbuf_rdy = 1; /* Hit. */ } else FD_SET(fd, wset_ans); /* 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_ans); } else if( elt != SCHFALSE ) { /* It better be a port. */ fd = EXTRACT_FIXNUM(*PortFd(elt)); FD_SET(fd, rset_ans); } 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. */ /* select1() is defined in sysdep.h -- bogus compatibility macro. */ nfound = select1(max_fd+1, rset_ans, wset_ans, eset_ans, 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 -1; else { /* EINTR, but we have hits on buffered ports to report. */ FD_ZERO(rset_ans); /* This should never happen -- */ FD_ZERO(wset_ans); /* EINTR on a zero-sec select() */ FD_ZERO(eset_ans); /* -- but I'm paranoid. */ } /* OR together the buffered-io ready sets and the fd ready sets. */ if( rbuf_rdy ) or2_fdset(rset_ans, &rset_bufrdy, max_fd); if( wbuf_rdy ) or2_fdset(wset_ans, &wset_bufrdy, max_fd); return 0; } /* 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); } /* 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; } /* Overwrite every inactive element in the vector with #f; ** Return count of active elements. */ static int clobber_inactives(scheme_value portvec, fd_set *fdset) { int count = 0; int i = VECTOR_LENGTH(portvec); while( --i >= 0 ) { scheme_value elt = VECTOR_REF(portvec, i); if( elt != SCHFALSE ) { 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. */ ++count; } else VECTOR_REF(portvec, i) = SCHFALSE; /* Clobber. */ } } return count; } /* These two functions are the entry points to this file. ********************************************************* */ /* Copy active elts back to the front of their vector; ** Return error indicator & number of hits for each vector. */ scheme_value select_copyback(scheme_value rvec, scheme_value wvec, scheme_value evec, scheme_value nsecs, int *r_numrdy, int *w_numrdy, int *e_numrdy) { fd_set rset, wset, eset; if( do_select(rvec, wvec, evec, nsecs, &rset, &wset, &eset) ) { *r_numrdy = *w_numrdy = *e_numrdy = 0; return ENTER_FIXNUM(errno); } *r_numrdy = copyback_fdvec(rvec, &rset); *w_numrdy = copyback_fdvec(wvec, &wset); *e_numrdy = copyback_fdvec(evec, &eset); return SCHFALSE; } /* Overwrite non-active elements in the vectors with #f; ** return error indicator & number of hits for each vector. */ scheme_value select_filter(scheme_value rvec, scheme_value wvec, scheme_value evec, scheme_value nsecs, int *r_numrdy, int *w_numrdy, int *e_numrdy) { fd_set rset, wset, eset; if( do_select(rvec, wvec, evec, nsecs, &rset, &wset, &eset) ) { *r_numrdy = *w_numrdy = *e_numrdy = 0; return ENTER_FIXNUM(errno); } *r_numrdy = clobber_inactives(rvec, &rset); *w_numrdy = clobber_inactives(wvec, &wset); *e_numrdy = clobber_inactives(evec, &eset); return SCHFALSE; }