/* 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. */ /* 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 int errno; static void or2_fdset(fd_set *x, fd_set *y, int max_elt); static int copyback_fdvec(s48_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(s48_value rvec, s48_value wvec, s48_value evec, s48_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 = S48_VECTOR_LENGTH(rvec); for(i=nelts; --i >= 0; ) { s48_value elt = S48_VECTOR_REF(rvec,i); int fd; fd = s48_extract_fixnum(elt); FD_SET(fd, rset_ans); max_fd = max(max_fd, fd); } /* Scan the writevec elts. */ nelts = S48_VECTOR_LENGTH(wvec); for(i=nelts; --i >= 0; ) { s48_value elt = S48_VECTOR_REF(wvec,i); int fd; fd = s48_extract_fixnum(elt); FD_SET(fd, wset_ans); max_fd = max(max_fd, fd); } /* Scan the exception-vec elts. */ nelts = S48_VECTOR_LENGTH(evec); for(i=nelts; --i >= 0; ) { s48_value elt = S48_VECTOR_REF(evec,i); int fd; fd = s48_extract_fixnum(elt); FD_SET(fd, eset_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 ( S48_FIXNUM_P(nsecs) ) { timeout.tv_sec = s48_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 = select(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(s48_value portvec, fd_set *fdset) { int vlen = S48_VECTOR_LENGTH(portvec); int i, j=0; for( i = -1; ++i < vlen; ) { s48_value elt = S48_VECTOR_REF(portvec, i); int fd = s48_extract_fixnum((S48_FIXNUM_P(elt)) ? elt : (1 / 0)); /* JMG *PortFd(elt));*/ if( FD_ISSET(fd,fdset) ) { FD_CLR(fd,fdset); /* In case luser put elt in multiple times. */ S48_VECTOR_SET(portvec, j, elt); j++; } } return j; } /* Overwrite every inactive element in the vector with #f; ** Return count of active elements. */ static int clobber_inactives(s48_value portvec, fd_set *fdset) { int count = 0; int i = S48_VECTOR_LENGTH(portvec); while( --i >= 0 ) { s48_value elt = S48_VECTOR_REF(portvec, i); if( elt != S48_FALSE ) { int fd = s48_extract_fixnum((S48_FIXNUM_P(elt)) ? elt : (1/0)); /* JMG *PortFd(elt));*/ if( FD_ISSET(fd,fdset) ) { FD_CLR(fd,fdset); /* In case luser put elt in multiple times. */ ++count; } else S48_VECTOR_SET(portvec, i, S48_FALSE); /* 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. */ s48_value select_copyback(s48_value rvec, s48_value wvec, s48_value evec, s48_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 s48_enter_fixnum(errno); } *r_numrdy = copyback_fdvec(rvec, &rset); *w_numrdy = copyback_fdvec(wvec, &wset); *e_numrdy = copyback_fdvec(evec, &eset); return S48_FALSE; } /* Overwrite non-active elements in the vectors with #f; ** return error indicator & number of hits for each vector. */ s48_value select_filter(s48_value rvec, s48_value wvec, s48_value evec, s48_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 s48_enter_fixnum(errno); } *r_numrdy = clobber_inactives(rvec, &rset); *w_numrdy = clobber_inactives(wvec, &wset); *e_numrdy = clobber_inactives(evec, &eset); return S48_FALSE; }