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 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 POLLIN|POLLHUP POLLIN|POLLHUP POLLIN|POLLHUP POLLIN|POLLHUP POLLIN
FreeBSD 4.2, 4.5, 4.9, 6.2 POLLIN|POLLHUP POLLIN POLLIN POLLIN POLLIN
NetBSD 1.6.1 POLLIN|POLLHUP POLLIN POLLIN POLLIN POLLIN
OpenBSD 3.1 POLLIN POLLIN POLLIN POLLIN POLLIN
AIX 4.3, 5.1L POLLIN 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
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.

Apple, IBM and OpenBSD at least manage to provide consistent behavior within a given OS version; every else seems to think that the semantics of poll() should depend on what kind of file you asked it about.

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.

Notes

  1. The “pipe at EOF” was created by calling pipe() and then calling close() for the writing end.
  2. The “socketpair” tests are a UNIX-domain stream socket, with one end closed; with one end shut down for writing (and polling the other end); and with one end shut down for reading (and polling that end).
  3. The “regular file” was an empty file freshly created for the purpose. In Linux at least polling such a file will set POLLIN even if it is write-only!
  4. poll.c is the program I used when I updated the results in March 2004. If you run it on your own system, mail me the output.

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