Current-Users archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Partial reads on unix domain sockets



Some time last year, probably late summer or autumn, a change was made
that caused transfer of small chunks of data over unix domain sockets to
have a higher chance of resulting in a read() getting only part of the
chunk.

While there is no guarantee of a one to one relationship between writes
and reads, it seems that some applications expect this.  In my case, it
was jack (pkgsrc/audio/jack) that failed.  It comes with, among other
things, a daemon, jackd, and a library for use by clients wishing to
connect to it.  Communication between jackd and its clients became
impossible with this change, because the code in jack expects to be able
to exchange C structs between server and clients.  The jackd server has
a thread that uses poll() to wait for available packets from clients,
and when something arrives, it is read with code like this example:

   if (read (client_fd, &req, sizeof(req)) != sizeof(req)) {
      jack_error ("cannot read ACK connection request from client");
      return -1;
   }

The client_fd is an open unix domain stream socket, and it is *not* in
non-blocking mode.  The structs being transfered are of various sizes,
and can, from a casual inspection of the header files, be up to a couple
of hundred bytes long.

Data is written to the sockets using code like this:

   if (write (reply_fd, &req, sizeof(req)) < (ssize_t)sizeof(req)) {
      jack_error ("cannot write request result to client");
      return -1;
   }

Meanwhile, in the client library, the code at the other end of this
communication is simply:

   if (write (fd, &req, sizeof(req)) != sizeof(req)) {
      jack_error ("cannot write event connect request to server (%s)",
                  strerror (errno));
      close (fd);
      return -1;
   }

   if (read (fd, &res, sizeof(res)) != sizeof(res)) {
      jack_error ("cannot read event connect result from server (%s)",
                  strerror (errno));
      close (fd);
      return -1;
   }

Obviously, poll() will return, with information about available data,
before the entire chunk written by the other end is available.

I haven't filed a PR on this, as it isn't technically an error in
NetBSD.  However, if there is a wide-spread belief out there that code
such as this will "just work" (I'm guessing it "just works" on Linux,
just like it does on NetBSD < 10), and it's not otherwise detrimental to
have the data from a single write() call all be available to the reader
of the socket before triggering a select() or poll() that's waiting for
it, then maybe such an adjustment should be considered.

-tih
-- 
Most people who graduate with CS degrees don't understand the significance
of Lisp.  Lisp is the most important idea in computer science.  --Alan Kay


Home | Main Index | Thread Index | Old Index