Re: Partial reads on unix domain sockets

2021-04-07 Thread Rhialto
On Wed 07 Apr 2021 at 13:43:52 +0200, Tom Ivar Helbekkmo wrote:
> 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:

Shouldn't code that expects that open a SOCK_SEQPACKET socket instead of
SOCK_STREAM?

(Or SOCK_DGRAM perhaps, since socket(2) seems to say that SOCK_SEQPACKET
doesn't exist for PF_LOCAL)

-Olaf.
-- 
___ Q: "What's an anagram of Banach-Tarski?"  -- Olaf "Rhialto" Seibert
\X/ A: "Banach-Tarski Banach-Tarski." -- rhialto at falu dot nl



signature.asc
Description: PGP signature


Re: Partial reads on unix domain sockets

2021-04-07 Thread Christos Zoulas
In article ,
Tom Ivar Helbekkmo   wrote:
>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, , 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, , 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, , sizeof(req)) != sizeof(req)) {
>  jack_error ("cannot write event connect request to server (%s)",
>  strerror (errno));
>  close (fd);
>  return -1;
>   }
>
>   if (read (fd, , 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.

Can you please file a PR with an example to keep track of it. It is a behavior
change after all and we should understand why it happened.

christos



Partial reads on unix domain sockets

2021-04-07 Thread Tom Ivar Helbekkmo
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, , 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, , 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, , sizeof(req)) != sizeof(req)) {
  jack_error ("cannot write event connect request to server (%s)",
  strerror (errno));
  close (fd);
  return -1;
   }

   if (read (fd, , 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