Re: Partial reads on unix domain sockets
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
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
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