Subject: kern/27185: kqueue: EOF on pipe gains no EVFILT_READ event
To: None <gnats-bugs@gnats.NetBSD.org>
From: Christian Biere <christianbiere@gmx.de>
List: netbsd-bugs
Date: 10/07/2004 22:44:21
>Number: 27185
>Category: kern
>Synopsis: kqueue: EOF on pipe gains no EVFILT_READ event
>Confidential: no
>Severity: non-critical
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Thu Oct 07 20:45:01 UTC 2004
>Closed-Date:
>Last-Modified:
>Originator: Christian Biere
>Release: NetBSD 2.0G
>Organization:
>Environment:
System: NetBSD cyclonus 2.0G NetBSD 2.0G (STARSCREAM) #0: Tue Jul 20 02:41:48 CEST 2004 bin@cyclonus:/usr/obj/sys/arch/i386/compile/STARSCREAM i386
Architecture: i386
Machine: i386
>Description:
When using pipes for IPC, closing the write end of pipe doesn't cause
a EVFILT_READ kqueue event on the reader's side. It works using the
traditional poll() interface i.e., the reader gets a POLLHUP on the
corresponding file descriptor. If checked this on NetBSD 2.0G with
sources as of September 24th, too.
>How-To-Repeat:
The following program demonstrates the problem. When compiled with
-DUSE_KQUEUE, the child process doesn't terminate as it receives no
event when the parent process exits which implicitely closes the
pipe. If compiled to use poll() the child receives a POLLHUP event
and terminates.
Note the #if 0 ... #endif part, though. If the write end is closed before
the fork(), the child will also receive an event using kqueue(). So
this looks really like a bug and not like a semantic difference.
/*
* To see the effect using when kqueue():
* cc -DUSE_KQUEUE pipe_test.c -o pipe_test
*
* The same using poll():
* cc pipe_test.c -o pipe_test
*/
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
static void fatal(const char *s);
#ifdef USE_KQUEUE
#include <sys/event.h>
#include <sys/time.h>
static void
wait_for_eof(int fd)
{
int kq;
struct kevent kev;
static const struct timespec ts = { 0, 0 };
kq = kqueue();
if (-1 == kq)
fatal("kqueue");
EV_SET(&kev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
if (kevent(kq, &kev, 1, 0, 0, &ts))
fatal("kevent()");
while (-1 == kevent(kq, NULL, 0, &kev, 1, NULL)) {
if (errno != EINTR)
fatal("kevent()");
}
}
#else /* !USE_KQUEUE */
#include <poll.h>
static void
wait_for_eof(int fd)
{
struct pollfd pfd;
pfd.fd = fd;
pfd.events = POLLIN;
while (-1 == poll(&pfd, 1, INFTIM)) {
if (errno != EINTR)
fatal("poll()");
}
}
#endif
static void
fatal(const char *s)
{
perror(s);
exit(EXIT_FAILURE);
}
int main(void)
{
int fds[2];
if (pipe(fds))
fatal("pipe()");
switch (fork()) {
case -1:
fatal("fork()");
break;
case 0: /* Child */
close(fds[1]);
#if 0
/* If the parent process exits before wait_for_eof() is reached,
* the child does actually receive a EVFILT_READ event. */
sleep(8);
#endif
wait_for_eof(fds[0]);
printf("Child terminates now\n");
exit(EXIT_SUCCESS);
break;
default: /* Parent */
close(fds[0]);
}
sleep(4);
printf("Parent terminates now\n");
exit(EXIT_SUCCESS);
}
>Fix:
>Release-Note:
>Audit-Trail:
>Unformatted: