poll() and EOF

The Question

poll() is a UNIX system call which allows a program to monitor multiple file descriptors and detect when they are readable or writable. A reasonable question to ask is: what does it do when a file descriptor reaches EOF (end of file)? Should it set POLLIN (as one might expect from select()), POLLHUP, or both?

The Specification

The Single UNIX Specification says, in what is basically a copy of the SVR4 poll() man page:

POLLIN Data other than high-priority data may be read without blocking. For STREAMS, this flag is set in revents even if the message is of zero length.
POLLHUP The device has been disconnected. This event and POLLOUT are mutually exclusive; a stream can never be writable if a hangup has occurred. However, this event and POLLIN, POLLRDNORM, POLLRDBAND or POLLPRI are not mutually exclusive. This flag is only valid in the revents bitmask; it is ignored in the events member.

It’s not immediately clear from this whether “disconnected” means any end-of-file condition, or refers only to things like modem hangups. (When you type ^D at a program, only that program sees EOF - the calling shell doesn’t lose its input too. That only happens when the connecting modem hangs up, the telnet session is terminated, or whatever.)

Stevens thought the latter, and said in APUE that this implies that plain EOFs should be signalled by POLLIN, not POLLHUP.

The Implementations

Do the implementors agree? Unfortunately, my tests show that the behaviour is neither consistent within a single implementation, nor between different versions of the same implementation, and certainly not across platforms:

Platform Version File Descriptor
pipe socketpair regular file
closed SHUT_WR SHUT_RD
Linux 2.2.19 POLLHUP POLLHUP ? ? POLLIN
2.4.25, 2.6.18, 2.6.32, 3.2.0 POLLHUP POLLIN|POLLHUP POLLIN POLLIN POLLIN
SunOS 5.6, 5.7, 5.8, 5.10 POLLHUP POLLIN POLLIN POLLIN POLLIN
Mac OS 10.3.4 POLLIN POLLIN POLLIN POLLIN POLLIN
10.4.11, 10.6.4, 10.7.4 POLLIN|POLLHUP POLLIN|POLLHUP POLLIN|POLLHUP POLLIN|POLLHUP POLLIN
FreeBSD 4.2, 4.5, 4.9, 6.2 POLLIN|POLLHUP POLLIN POLLIN POLLIN POLLIN
8.0, 9.0 POLLIN|POLLHUP POLLIN|POLLHUP POLLIN POLLIN POLLIN
NetBSD 1.6.1 POLLIN|POLLHUP POLLIN POLLIN POLLIN POLLIN
OpenBSD 3.1 POLLIN POLLIN POLLIN POLLIN POLLIN
5.4 POLLIN|POLLHUP POLLIN|POLLHUP POLLIN POLLIN POLLIN
AIX 4.3, 5.1L POLLIN POLLIN POLLIN POLLIN POLLIN
5.3 POLLIN|POLLHUP POLLIN POLLIN POLLIN POLLIN
Cygwin POLLIN POLLHUP POLLHUP POLLERR POLLIN
HP-UX 10.20 POLLIN POLLIN ? ? POLLIN
11.00, 11.11, 11.23 POLLIN POLLIN|POLLHUP 0 POLLIN|POLLHUP POLLIN
11.31 POLLIN POLLIN|POLLHUP POLLIN POLLIN POLLIN
UnixWare 7.1.1 POLLHUP POLLIN|POLLHUP ? ? POLLIN
IRIX 6.4, 6.5 POLLIN|POLLHUP POLLIN POLLIN POLLIN POLLIN
OSF1 4.0 POLLIN POLLIN|POLLHUP ? ? POLLIN

“?” in the table reflects combinations I didn’t have convenient access to when doing the tests. Feel free to send updates (see below for a test program). The other entries are the bits set in revents when the file descriptor as described is at end of file.

In all cases, poll is asked to check for POLLIN on the reading end of the connection (where there’s a distinction).

The pipe column means that the other end of the pipe was closed. For the socketpair columns, the first means that the other end of the socket was closed; the second that it was shut down for writing; and the third that this end of the socket was shut down for reading (in which case, polling for read is probably a bug, but it’s interesting to know what happens in any case).

The final regular file column describes what happens when you poll a regular (disk) file. This is pretty pointless as they are always considered immediately readable (perhaps even if they are opened write-only, e.g. on Linux).

HPUX shutdown(2) appears to be broken.

On Cygwin, reading from a socket that you’ve shutdown with SHUT_RD produces ESHUTDOWN rather than EOF, and POLLERR if you poll it. This sees fair enough to me.

Conclusions

The lesson for authors of portable code is clear: test for both POLLIN and POLLHUP, and rely on the subsequent read() to tell you whether you reached EOF. Also you have to avoid at least one of shutdown() and HPUX.

What should implementors do? I think interpreting POLLHUP as referring to modem hangups &c rather than just end of file is most accurate interpretation of the SUS specification, and that implementations should therefore set POLLIN alone for other kinds of end of file.

Code

poll.c is the program I use to update these results. If you run it on your own system, mail me the output. Thankyou to all the people who’ve submitted results so far.


© 2001, 2004, 2007, 2010 Richard Kettlewell | RJK | Contents