This is the mail archive of the guile@cygnus.com mailing list for the guile project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: [PATCH] solaris socket communications bug workaround



I think this bug and its solution might be of interest to anyone
writing servers in Guile.

> We have found that guile provokes a problem with communicating over
> sockets, which we believe is due to a problem in solaris libc. Below
> is a patch that implements a workaround.

I don't really like this solution, but here goes:

It's not a bug in the Solaris C library.  Basically, Guile is running
afoul of the following paragraph in the man page for fdopen, which
Guile's `accept' function uses to turn the socket's file descriptor
into a buffered I/O port:

     When a file is opened for update, both input and output  may
     be done on the resulting stream.  However, output may not be
     directly followed by input without an intervening  fflush(),
     fseek(),  fsetpos(),  or  rewind(),  and  input  may  not be
     directly followed by output without an intervening  fseek(),
     fsetpos(),   or   rewind(),   or  an  input  operation  that
     encounters end-of-file.

Basically, they're cheap, and the stdio stream only has one buffer,
and one pointer into that buffer, for both input and output.  So you
have to empty the buffer whenever you change directions.  When
switching from writing to reading, this isn't a problem --- you flush
the buffer, and away you go.  However, in order to switch from reading
to writing, you have to turn off all input buffering, or else the
writing will wipe out your unread input.

Anyway, the long and the short of it is: for now, just call (fseek
PORT 0 SEEK_CUR) between reading and writing, and (force-output PORT)
between writing and reading, and things should work.

For example, the following version of your Guile server works
correctly for me:

    (define lsocket (socket AF_INET SOCK_STREAM 0))
    (define portno 8765)
    (setsockopt lsocket SOL_SOCKET SO_REUSEADDR 1)
    (bind lsocket AF_INET INADDR_ANY portno)
    (listen lsocket 5)
    (define newconnection #f)

    (display "Waiting for connections...\n")
    (set! newconnection (accept lsocket))
    (let* ((handle newconnection) (thissock (car handle)))
      (display "Waiting for input...\n")
      (let ((input (read thissock)))
	(display "Has read: ")
	(write input)
	(newline)
	(fseek thissock 0 SEEK_CUR)
	(write input thissock)
	(newline thissock)))

In the long run, I want to put some logic into Guile to handle this
transparently, but it requires a non-trivial change to the port
system to do it right, so it's not going to be in 1.3.