[Openmcl-devel] Transfer the contents of a file to a tcp stream

Gary Byers gb at clozure.com
Fri May 13 05:29:32 PDT 2005



On Fri, 13 May 2005, [ISO-8859-1] Christian Nybø wrote:

>
> On May 5, 2005, at 22:55, Gary Byers wrote:
>> Maybe something like:
>> 
>> (let* ((bufsize 8192))
>>   (ccl::%stack-block ((buf bufsize)
>>                       (iovec (* 4 (ccl::record-length :iovec))))
>>     ;; Set up each element of the iovec array to point at 1/4 of the
>>     ;; buffer.
>>     (dotimes (i 4)
>>       (with-macptrs ((p (%inc-ptr iovec (* i (ccl::record-length 
>> :iovec)))))
>>         (setf (pref p :iovec.iov_base) (%inc-ptr buf (* i 2048))
>>               (pref p :iovec.iov_len) 2048)))
>>     ...
>>     (loop
>>       (let* ((nread (#_read file-fd buf bufsize)))
>>         ((cond ((= nread bufsize)
>>                 ;; Got a full buffer, write to socket via #_writev
>>                 (#_writev socket-fd iovec 4))
>>                ((> nread 0)
>>                 ;; Got less than a full buffer.  Either adjust
>>                 ;; the count of iovecs/the iov_len of the last one,
>>                 ;; or just do a single #_write as before.
>>                )
>>                ((= nread 0) (return))
>>                (t (error ...))))))))
>
> I wrapped the writev that is called when (= nread bufsize) in a loop.  It 
> gets stuck quite often - the errno is -35, whose strerror is "Resource 
> temporarily unavailable."
> What should the program do when writev returns -35?
> -- 
> chr


I think that the usual thing to do is to use #_select to wait until
output is possible on the file descriptor.  (Conceptually, you -could-
just try again - the error condition is transient - but doing so would
be something like busy-waiting.)

Way back when (when OpenMCL's sockets code was first written) it made
sense to set the socket's file descriptor to "non-blocking" mode: if
a relatively slow device (like a network socket) is busy, a socket
in non-blocking mode would return this error (#$EAGAIN = 35) and the lisp
scheduler could try to find some other thread to run.  (If the thread
that was trying to do I/O blocked in the OS kernel waiting for the
device to be ready, the lisp scheduler had no opportunity to run and
all threads in the application stopped until the I/O operation completed.)

That's not really an issue anymore (since there's no lisp scheduler
anymore  ...); unless the thread that's trying to do I/O has something
better to do while it's waiting for the network driver, it should probably
just let the OS kernel put it to sleep until the network's available
and let some other thread/the network driver get some CPU cycles. (In
your test cases, it might be a good idea to let "wget" run for a while,
to read some of those network packets that're queued up for it.)

The lisp I/O code (mostly in "ccl:level-1;l1-streams.lisp") uses a
macro - CCL::WITH-EAGAIN - to handle this situation.  (It's handled
by using #_select to wait - forever - until the device is ready.)

It's certainly simpler (and probably at least a little faster) to
leave the socket in blocking mode in the first place.  This'll happen
if the socket's created with CCL:*MULTIPROCESSING-SOCKET-IO* set to
NIL. (That variable really means something like
CCL:*MULTIPROCESSING-IN-THE-OLD-TRADITIONAL-LISP-SCHEDULER-SENSE-SOCKET-IO*,
and it should probably be phased out, or at least have its default
value changed.  The only cases that I can think of where non-blocking
mode is still useful are those where the calling thread has something
else useful to do if the I/O operation isn't currently possible.)


More information about the Openmcl-devel mailing list