scsh-0.5/scsh/select1.c

197 lines
5.6 KiB
C
Raw Normal View History

1995-10-13 23:34:21 -04:00
/* 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 <sys/types.h>
#if defined(HAVE_SYS_SELECT_H)
# include <sys/select.h>
#endif
#include <sys/time.h>
#include <errno.h>
#include <stdio.h>
#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);
}