[comp.unix.questions] How do I recover my pipe on my Parent's Death?

frank@odetics.com (Frank Merrow) (05/22/91)

Hi,

I am attempting to monitor a number of file descriptors for input using
select().  Below I have included a piece of my code.  Actually things
work pretty good, EXCEPT for some reason when one of the processes
(my parent in this case) dies.  (If it matters the file descriptors
were originally created with pipe() and dup2()ed to STDIN and STDOUT
after the fork().)

At any rate once the parent dies, select() immediately releases, but
the ioctl() shows "nothing" in the pipe so I go back to the select()
which immediately releases and so on.  What do I need to do to
detect/clear the condition.  Note that I tried setting O_NDELAY
expecting the read() to return -1 and some kind of a nice error code.
However it just returns zero and the condition STILL is not cleared.

Frank
frank@odetics.com or uunet!odetics!frank

typedef
   struct
      {
      int fd;
      void (*routine)();
      } FD_LIST;

. . .

num_fd_list        = 1;
fd_list[0].fd      = STDIN;
fd_list[0].routine = xfp_request;

/* Other fd's to be added at a later time.				*/

/* Ok, I loop waiting for input and then processing it.			*/

for (keep_going=TRUE;keep_going;)
   {
   if (select(numfds,&readfds,NULL,NULL,NULL) < 1)
      fatal("select() error in main(xfs)");
   PRINT_TRACE("select() released");
   for (i=0;i < num_fd_list;i++)
      {
      if (ioctl(fd_list[i].fd,FIONREAD,&nbytes) != 0)
	 fatal("ioctl() error in main(xfs)");
      if (nbytes > 0)
	 (*fd_list[i].routine) (i);
      }
   }

jik@athena.mit.edu (Jonathan I. Kamens) (05/22/91)

  First of all, I don't see any code there where you properly initialize
readfds to tell select() which file descriptors you are interested in.  Are
you doing that?

  Second, why are you using an FIONREAD ioctl to check each file descriptor,
when select() will tell you in readfds (that is, after all, why you pass it a
pointer to the fd_set -- so that it can modify it) which file descriptors it
has determined are ready to be read?  Of course, this means that you have to
reinitialize readfds each time you call select(), which brings us back to the
first paragraph above.

  Third, select() indicates that a file descriptor has reached EOF by
indicating that it is ready to be read.  You are expected to then read() the
file descriptor, notice that read() returns 0, and conclude that the file
descriptor has reached EOF, at which point you should close it and remove it
from the list of descriptors that select() examines.

  The ioctl() you are doing is completely correct in returning 0 in response
to FIONREAD on a descriptor which has reached EOF -- if it's at EOF, there is
no data to be read on it.

-- 
Jonathan Kamens			              USnail:
MIT Project Athena				11 Ashford Terrace
jik@Athena.MIT.EDU				Allston, MA  02134
Office: 617-253-8085			      Home: 617-782-0710

mouse@thunder.mcrcim.mcgill.edu (der Mouse) (05/24/91)

In article <1991May21.213431.2193@odetics.com>, frank@odetics.com (Frank Merrow) writes:

> I am attempting to monitor a number of file descriptors for input
> using select().  Below I have included a piece of my code.  Actually
> things work pretty good, EXCEPT for some reason when one of the
> processes (my parent in this case) dies.

> At any rate once the parent dies, select() immediately releases, but
> the ioctl() shows "nothing" in the pipe so I go back to the select()
> which immediately releases and so on.  What do I need to do to
> detect/clear the condition.
(Questions generally end with `?', not `.'.)

select() for input returns when a read() call will not block.  This is
not always the same as returning when there is something to read.  In
your case, you've reached EOF on the pipe; if you call read() you'll
see that it returns 0.  Naturally FIONREAD returns 0, because there's
nothing there.

As for detecting it, you already have: select() indicates that it's
ready for input, but there's nothing there.  To clear it, you can't;
that pipe is forever going to show EOF.  You can't do much with it but
close it.

> for (keep_going=TRUE;keep_going;)
>    {
>    if (select(numfds,&readfds,NULL,NULL,NULL) < 1)
>       fatal("select() error in main(xfs)");
>    PRINT_TRACE("select() released");
>    for (i=0;i < num_fd_list;i++)
>       {
>       if (ioctl(fd_list[i].fd,FIONREAD,&nbytes) != 0)
> 	 fatal("ioctl() error in main(xfs)");
>       if (nbytes > 0)
> 	 (*fd_list[i].routine) (i);
>       }
>    }

I note three problems with this code immediately.

1) The descriptor masks will normally be changed by select(), so you
   should reconstruct readfds each time around the loop.

2) The last three arguments to select() are pointers; unless you don't
   care about portability to non-prototype-capable compilers, you
   should pass pointers.

3) You should *use* the descriptor mask returned by select.  RTFM; when
   select returns the descriptor mask has been modified so that only
   those bits corresponding to ready-to-read file descriptors are set.

A sketch of what it might look like after these changes, assuming
numfds is correctly set (ie, is larger than all file descriptors in the
fd_list array):

for (keep_going=TRUE;keep_going;)
   {
   FD_ZERO(&readfds);
   for (i=0;i<num_fd_list;i++)
      FD_SET(fd_list[i].fd,&readfds);
   if (select(numfds,&readfds,(fd_set *)NULL,(fd_set *)NULL,(struct timeval *)NULL) < 1)
      fatal("select() error in main(xfs)");
   PRINT_TRACE("select() released");
   for (i=0;i < num_fd_list;i++)
      {
      if (FD_ISSET(fd_list[i].fd,&readfds))
	 (*fd_list[i].routine) (i);
      }
   }

If your system is so old that it doesn't have the FD_ZERO, FD_SET, etc,
macros, you can use an include file I wrote for just such systems:

#ifndef FD_SETSIZE

/* We presumably have a system without the 4.3 fd_set stuff.
   This includes older Sun releases which have a typedef for fd_set but
   none of the associated baggage like FD_SET, FD_CLR, etc. */

#define fd_set fixed_fd_set
typedef struct {
	  int fds[1];
	  } fixed_fd_set;
#define FD_SETSIZE (8*sizeof(int)) /* at least */
#define FD_SET(fd,set) ((set)->fds[0]|=(1<<(fd)))
#define FD_CLR(fd,set) ((set)->fds[0]&=~(1<<(fd)))
#define FD_ISSET(fd,set) ((set)->fds[0]&(1<<(fd)))
#define FD_ZERO(set) ((set)->fds[0]=0)

#endif

This include file is slightly nonportable (it passes a pointer to a
structure instead of a pointer to int), but I believe all systems that
run systems with select(), but without the correct macros, will accept
it and run just fine.

					der Mouse

			old: mcgill-vision!mouse
			new: mouse@larry.mcrcim.mcgill.edu