[Larceny-users] portio.sch - do loop eof, n-bytes read check sequencing

Ray Racine ray.racine at comcast.net
Tue Feb 5 00:47:37 EST 2008


I ran across this one when I did a custom io port for the http protocol
which one can use to wrap a socket port.

In general, in the http protocol, one always knows how many bytes to
read for the payload.  But when I tried to read n bytes via
get-bytevector-n! I was blocking until the peer closed their side of the
connection.   This would cause several seconds of delay.  

I could never figure out why.

The answer is that in portio.sc, in get-bytevector-n! the loop which
reads into the buffer looks like this.

(define (get-bytevector-n! p bv start count)
  (if (and (input-port? p)
           (binary-port? p)
           (bytevector? bv)
           (fixnum? start)
           (fx<= 0 start)
           (fixnum? count)
           (fx<= 0 count)
           (fx<= (fx+ start count) (bytevector-length bv)))
      (do ((n    (fx+ start count))
           (i    start      (fx+ i 1)))
          ((or (port-eof? p) (fx= i n))
           (- i start))
        (bytevector-set! bv i (get-u8 p)))
      (portio/illegal-arguments 'get-bytevector-n! p bv start count)))


It loops until eof is reached OR when the required n bytes have been.
The checks occur in that order.  

Lets say I do a http get on a socket to some server.  The server sends
back a http header plus a 10 byte payload response.  After reading the
HTTP header off of the connection, from the header's Content-Length
value I know the payload is 10 bytes in size. 

When the 10th byte has been read the ports internal mainbuf (the one in
iosys.sch) is exactly empty.  At this point I'd like get-bytevector-n!
to return as I have no more bytes to read and (fx= i n) is true.  

However, first the loop does a (port-eof? p) which causes a io/get-u8 to
occur in peek mode. But socket port's buffer is empty, causing a fill
buffer to occur, but there are no more bytes to read, i.e., there is
nothing to peek, so we sit blocked until the peer decides to close the
socket.  When I am in tasking + socket non-block mode the task sleeps
until poll tells the task there are more bytes available (never happens)
or poll triggers on a the socket close.  Since the 10 bytes have already
been read there is no need to sit blocked.

The answer is for the 'do' loop exit check first see if n bytes have
been read, and then check if eof has been reached on the port.

Specifically, 

((or (port-eof? p) (fx= i n))

should be, 

((or (fx= i n) (port-eof? p))

given the left-to-right sequencing of the 'or'.

Similarly in get-bytevector-n.


Thanks,


Ray




More information about the Larceny-users mailing list