[comp.unix.programmer] Using select on FIFO's

blarsen@spider.uio.no (Bjorn Larsen) (05/30/91)

Yesterday, I wanted to use select(2) to wait on a number of FIFO's
that were opened for reading. It turned out to be harder than I
expected.

Originally, the program opened the FIFO's with O_NONBLOCK, and tried
to read them occationally. It turns out that select() on such a file
descriptor returns immediately, indicating that the fd was ready.  In
a sense, that is true -- the fd is ready so far that I can take a
read() on it without blocking. But what I expected select() to do was
to tell me wether there was data on the FIFO, not wether it is
possible to issue a read() on the fd.

The program is a deamon, and the FIFO's will be opened and written to
by other programs every once in a while. So opening the FIFO's for
blocking read isn't such a hot idea eigther; it will cause the daemon
to block, waiting for a process to open the FIFO for writing.

So I came up with the following 'solution':

	int fd, wfd;

/* Open the FIFO for nonblocking read */
	fd = open("/tmp/FIFO", O_RDONLY|O_NONBLOCK);

/* Open the FIFO for write (won't hang, since it is open for read) */
	wfd = open("/tmp/FIFO", O_WRONLY);

/* Close the read descriptor */
	close(fd);

/* Open the FIFO for blocking read */
	fd = open("/tmp/FIFO", O_RDONLY);

After these acrobatics, the file descriptor 'fd' can be used with
select().

It seems to work OK, but is this the 'right' way to do this?
Are there any sideeffects of this that I haven't thought about?
And is there a more elegant way to go about it?


---
Bjorn.Larsen@usit.uio.no                        "Specialization is for insects"
University Centre for Information Technology                     - Lazarus Long
University of Oslo, Norway

boyd@prl.dec.com (Boyd Roberts) (05/30/91)

Don't you just want to open the named pipe in non-blocking mode and then
use fcntl(2) to turn off the non-blocking so that read and select work
the way you want them to?


Boyd Roberts			boyd@prl.dec.com

``When the going gets wierd, the weird turn pro...''

les@chinet.chi.il.us (Leslie Mikesell) (05/30/91)

In article <BLARSEN.91May30090732@spider.uio.no> Bjorn.Larsen@usit.uio.no writes:
>Yesterday, I wanted to use select(2) to wait on a number of FIFO's
>that were opened for reading. It turned out to be harder than I
>expected.
[...]
>So I came up with the following 'solution':
>
>	int fd, wfd;
>
>/* Open the FIFO for nonblocking read */
>	fd = open("/tmp/FIFO", O_RDONLY|O_NONBLOCK);
>
>/* Open the FIFO for write (won't hang, since it is open for read) */
>	wfd = open("/tmp/FIFO", O_WRONLY);
>
>/* Close the read descriptor */
>	close(fd);
>
>/* Open the FIFO for blocking read */
>	fd = open("/tmp/FIFO", O_RDONLY);
>
>After these acrobatics, the file descriptor 'fd' can be used with
>select().

I don't have select(), but I've always just opened the FIFO with O_RDWR
on the reader side which (a) doesn't block during the open, and (b)
does cause the read()s to continue to block for data instead of returning
EOF after a writer has closed.  This allows the writers to come and
go as necessary.  If you can arrange for every action the daemon is
supposed to perform to be triggered by a write to the FIFO, you can
just let the read block.  Of course if you want to see EOF when the
writer exits, you can't do it this way.  I would expect select() to
wake up when the last writer closes, so you would control seeing this
by whether or not the reading process also opens for writing.  However,
if the reading process does not open for writing, after the FIFO has
been opened and closed by a writer it will return EOF immediately
instead of blocking whenever no writer has it open.  Select() shouldn't
block in this case either.

Les Mikesell
  les@chinet.chi.il.us

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (06/03/91)

In article <BLARSEN.91May30090732@spider.uio.no> Bjorn.Larsen@usit.uio.no writes:
> Originally, the program opened the FIFO's with O_NONBLOCK, and tried
> to read them occationally. It turns out that select() on such a file
> descriptor returns immediately, indicating that the fd was ready.  In
> a sense, that is true -- the fd is ready so far that I can take a
> read() on it without blocking. But what I expected select() to do was
> to tell me wether there was data on the FIFO, not wether it is
> possible to issue a read() on the fd.

select() normally tells you whether the I/O would block *if* the
descriptor were blocking. Unfortunately, ``blocking'' has one meaning in
System V, one meaning in BSD, two slightly different meanings in SunOS,
and yet another slightly different meaning in POSIX---and select() only
respects the BSD meaning. Don't expect select() to work properly if you
combine different nonblocking mechanisms.

Since you're using select(), you might as well not bother with NONBLOCK.
Just make sure to select() before every I/O operation. (Beware that if
there are multiple readers or writers on a pipe, select() can't
guarantee that the I/O won't block a moment later. In that case you
also have to set nonblocking before each I/O and unset it before
select(). It's at times like this that I wish for VMS I/O.)

---Dan

gwc@root.co.uk (Geoff Clare) (06/04/91)

les@chinet.chi.il.us (Leslie Mikesell) writes:

> I've always just opened the FIFO with O_RDWR
>on the reader side which (a) doesn't block during the open, and (b)

Although this works on most if not all current UNIX systems, it's not
required by POSIX, so applications using this method may not work
on some future systems.  For maximum portability to current and
future systems, I recommend using the following method to open both
sides of a FIFO to do blocking I/O:

	fd_rd = open("FIFO", O_RDONLY|O_NONBLOCK);
	fd_wr = open("FIFO", O_WRONLY);
	flags = fcntl(fd_rd, F_GETFL);
	fcntl(fd_rd, F_SETFL, flags & ~O_NONBLOCK);

with something like the following tucked away in a header:

#ifndef _POSIX_SOURCE
#define O_NONBLOCK O_NDELAY
#endif
-- 
Geoff Clare <gwc@root.co.uk>  (Dumb American mailers: ...!uunet!root.co.uk!gwc)
UniSoft Limited, London, England.   Tel: +44 71 729 3773   Fax: +44 71 729 3273

les@chinet.chi.il.us (Leslie Mikesell) (06/05/91)

In article <2729@root44.co.uk> gwc@root.co.uk (Geoff Clare) writes:

>Although this works on most if not all current UNIX systems, it's not
>required by POSIX, so applications using this method may not work
>on some future systems.  For maximum portability to current and
>future systems, I recommend using the following method to open both
>sides of a FIFO to do blocking I/O:
>
>	fd_rd = open("FIFO", O_RDONLY|O_NONBLOCK);
>	fd_wr = open("FIFO", O_WRONLY);
>	flags = fcntl(fd_rd, F_GETFL);
>	fcntl(fd_rd, F_SETFL, flags & ~O_NONBLOCK);

Is it required to work when the process opens with one fd for
reading and a different one for writing?  I do that in shell
scripts to make them block when no one else is writing to
the FIFO:
---simple shell script server---
setup code...
while read CMD P1 P2 P3 P4 P5 P6
do
 case "$CMD" in
  STOP)
    exit 0
     ;;
   whatever else...
  esac
done <FIFO 4>FIFO

I just start one of these in the background to schedule jobs and
other scripts periodically echo commands to it.  IMHO FIFO's would
be pretty useless if you couldn't get reasonable blocking to
happen from a shell script.  I also use the non-blocking mode
that happens after the first writer closes in some other scripts.
The setup is similar except for the 4>FIFO and a calling script
always writes a dummy command after starting the daemon script in
the background.  In this case the daemon program has something to
do in the body of the loop and only checks periodically for controlling
commands.  If nothing has been written since the last time around
the read returns EOF (nothing) and the loop continues.
Is this behaviour portable?

Les Mikesell
  les@chinet.chi.il.us