Subject: Re: pipes from FreeBSD and the NetBSD async I/O bug
To: Christos Zoulas <christos@zoulas.com>
From: Kevin B.Hendricks <khendricks@ivey.uwo.ca>
List: tech-kern
Date: 05/01/2001 14:23:15
Hi,

I tried everything I could with another test program similar to the one you 
just sent to me to get the read from the pipe to generate a sigio even when 
the write had produced an EAGAIN and I can never see a sigio at all.

This is funny.  Perhaps pipes are special cases?

Kevin



On Tuesday 01 May 2001 12:46, Christos Zoulas wrote:
> On Apr 30, 11:16pm, khendricks@ivey.uwo.ca (Kevin B.Hendricks) wrote:
> -- Subject: Re: pipes from FreeBSD and the NetBSD async I/O bug
> 
> | I am confused.  I have never seen async io used alone, only with 
non-blocking 
> | flag also set does it make sense.  IMHO You should not send a sigio to a 
> | process that asked to write n bytes to an fd when set up for non-blocking 
> | asynchronous io unless unless the write returned an error with errno set 
to 
> | EAGAIN.
> | 
> | The sequence I expect under async and nonblocking write roughly looks 
> | something like this:
> | 
> |    n = total number of bytes remaining to write
> |    here:
> |    c = attempted write of n bytes
> |    if c > 0 then n = n -c;  goto here
> |    if (c = -1 and errno = EAGAIN) then go do something else until sigio
> |            comes in indicating you are ready to write again
> | 
> | With async and nonblocking I do not expect to get a sigio every time 
> | something is read from the pipe unless the pipe is full causing the last 
> | write to return with EAGAIN.
> 
> But then it is really tough for the kernel to determine when to send
> sigio to the process. Assuming the size of the pipe buffer is 4096:
> 
> Scenario 1:
>     write 4096 bytes returns 4096 and the buffer is full.
>     read 1 byte.
>     Does the process get SIGIO? Well, it did not ask to write more
>     than the pipe buffer, and it might never want to write again so
>     one can claim that sending SIGIO is superfluous in this case, but
>     I think that it should.
> 
> Scenario 2:
>     write 8192 bytes returns 4096 and the buffer is full.
>     read 1 byte.
>     Does the process get SIGIO? I think so. There is space in the buffer
>     now to write more data.
> 
> Scenario 3:
>     write 8192 bytes returns 4096 and the buffer is full.
>     try writing the remaining 4096, returns -1 and errno = EAGAIN
>     read 1 byte.
>     Does the process get SIGIO? I think so. There is space in the buffer
>     now to write more data.
> 
> Scenario 4:
>     write 4 bytes, returns 4 and there is space in the buffer.
>     read 1 byte.
>     Does the process get SIGIO? The BSD sockets version thinks so.
>     The other implementations don't.
> 
> Things get more complicated if you involve process groups instead
> of single processes...
> 
> Consider the following variation to the program that Emmanuel posted.
> Running it with 8192 [on linux], says:
> 
> % ./a.out 8192
> written 4096 bytes
> reading from the pipe
> read 1 bytes
> wrote 1 bytes
> TEST SUCCESSFUL
> %
> 
> Even if I duplicate the write so that it returns EAGAIN on the
> second one [it is commented out right now] I still don't get a
> signal on linux and solaris. Now, I expect to get a signal because
> after the read, there is space in the pipe for me to write more
> data. What am I missing?
> 
> christos
> 
> #include <stdio.h>
> #include <stdlib.h>
> #include <unistd.h>
> #include <signal.h>
> #include <fcntl.h>
> 
> void io_sighandler (int sig) {
>   printf ("pid=%d got sigio\n", getpid ());
>   printf ("TEST FAILED\n");
>   exit (-1);
> }
> 
> int main(int argc, char** argv) {
>     struct sigaction aio;
>     int fdsync[2];
>     char c, *buf;
>     sigset_t set;
>     size_t size = atoi(argv[1]);
>     int err;
> 
>     sigemptyset(&set);
>     sigaddset(&set,SIGIO);
> 
> 
>     aio.sa_flags = SA_RESTART;
>     aio.sa_handler = io_sighandler;
>     sigemptyset(&aio.sa_mask);
>     if (sigaction(SIGIO, &aio, 0) == -1) {
>         printf("Error: Bad return value from sigaction call\n");
>         exit(1);
>     }
> 
>     if (pipe(fdsync) < 0) { /* fd for synchronization */
>       printf("Error: bad pipe call\n");
>       exit(1);
>     }
> 
>     /* now set the pipe write end to be non-blocking async */
>     fcntl(fdsync[1],F_SETFL, O_NONBLOCK | FASYNC);
>     fcntl(fdsync[1],F_SETOWN, getpid());
> 
>     buf = malloc(size);
>     memset(buf, 'A', size);
> 
>     err = write(fdsync[1], buf, size);
> /*
>     err = write(fdsync[1], buf, size);
> */
>     if (err < 0) {
>        printf("write() got err=%d\n", err);
>     }
>     printf ("written %d bytes\n", err);
> 
>     sleep (1);
> 
>     printf ("reading from the pipe\n");
>     err = read(fdsync[0], &c, 1);
>     printf ("read %d bytes\n", err);
>     err = write(fdsync[1], &c, 1);
>     printf ("wrote %d bytes\n", err);
> 
>     printf("TEST SUCCESSFUL\n");
>     exit(0);
> }
>