[Openmcl-devel] read-char-no-hang can hang

Gary Byers gb at clozure.com
Thu Nov 15 02:32:43 PST 2012

On Wed, 14 Nov 2012, Ron Garret wrote:

> When reading from /dev/some-slow-serial-device, read-char-no-hang can hang.  I think this is because LISTEN is defined to return NIL only on EOF, and the OS doesn't report the absence of input characters as EOF.  I tried to work around this by being devilishly clever and setting the underlying file descriptor to be non-blocking and using the FFI to call read() directly, but that seems to leave Lisp very confused:
> ? (read-char f)
>> Error: Expected newpos to be 3, fd is at 5
>> While executing: CCL::IO-FILE-IOBLOCK-ADVANCE, in process Listener(6).

That error message (and the reference to io-FILE-ioblock-advance) makes me think
that F is a FILE-STREAM, and it's not clear to me that it should be.  (One of 
the major differences between a FILE-STREAM and other kinds of fd-based streams
is that the former is seekable and the latter isn't.  I don't think of a stream
associated with a serial device as being seekable, and I'm fairly sure that
OPEN tries to return a non-seekable stream unless #_lseek seems to work on the fd.)

(A FILE-STREAM open in direction :IO is a bit different from other kinds of streams
that allow input and output: if you write an octet X to the stream (and it's likely
just sitting in buffer for the time being), set the file-position back one octet
and read an octet, you should get the same value (X) that you just wrote.)  In 
non-FILE-streams, input and output are generally independent of each other.  When
they are related (in the FILE-STREAM case), some fairly simple things related to
the file's position and length can be a little more compicated than they are
otherwise (and you sometimes need to do FORCE-OUTPUT while reading, etc.)  The
error you got has to do with some consistency checks in that code and it's saying
that the file's position isn't what we expect it to be, but serial devices don't
have a (seekable) position.

If FD is an open file descriptor, then (CCL::%UNIX-FD-KIND FD) will return a
keyword that tries to (broadly) describe the underlying device.   For an fd
open to a serial device, it should return :TTY; I suspect that it returns something
else in your example (you didn't OPEN a serial devices/TTY), or the stream was
created as if you'd opened a (disk) file even though the serial devices is a different
kind of animal.

That's a mystery.  Further mysteries include the device's baud rate, character
size and framing, and all of the (often obscure) input, output, and other options
that most of us likely haven't thought about (much) in a few decades.

I'd certainly forgotten most of whatever I knew about this until a few
years ago, when I needed to talk to a little testing device over a
serial line (it was actually a USB-to-serial converter.)  The code
that was used to do that is in "ccl:library;serial-streams.lisp"; it
doesn't do everything that you'd ever need to do to talk to an
arbitrary serial device (remember modems ?  Remember rs-232 signals
like "Data Terminal Ready" ? Remember trying to guess which device is
"DTE" and which is "DCE" ?), but it may be enough to get you started.
(I just glanced at the code and noticed that it redefines a PRINT-OBJECT
method on CCL::FD-STREAM; I think that it's more likely that the intent
was to define a new method on CCL::SERIAL-STREAM.  Whoever invented copy-and-paste
had no idea how dangerous it could be in the wrong hands.)

> Is there a way to reliably tell whether or not a call to read-char will hang on a serial device stream?

I think that the most reliable way involves using #_poll or #_select
with a timeout of 0; that'll return true if the fd is at EOF or if at
least one octet can be read from the fd without blocking.  (If that
returns true, attempts to read more than 1 octet might block, and
where the cutoff point is depends on the device and (possibly) on
additional options.  CCL::FD-INPUT-AVAILABLE-P (defined in
"ccl:level-1;l1-streams.lisp") use #_poll to do this on Unix.

A FILE-STREAM is assumed to never block (e.g, reading from it will always
return some data or EOF immediately.)  That may actually depend on the
speed and reliability of the underlying filesystem, but I think that
we generally assume that there's unread data available on a file stream
if the position we'd read from is < the file's size.  I haven't tried
to hook up that USB-serial converter to another machine that actually has 
a serial port to test this, but I'd guess that the READ-CHAR-NO-HANG behavior
you're seeing is related to the fact that the stream thinks it's closer to
being a FILE-STREAM than it likely should be.

> rg
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel

More information about the Openmcl-devel mailing list