From blgl@stacken.kth.se  Sun Sep 30 21:17:01 2007
Return-Path: <blgl@stacken.kth.se>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id 78DCE16A418
	for <FreeBSD-gnats-submit@freebsd.org>; Sun, 30 Sep 2007 21:17:01 +0000 (UTC)
	(envelope-from blgl@stacken.kth.se)
Received: from brev.stacken.kth.se (brev.stacken.kth.se [130.237.234.84])
	by mx1.freebsd.org (Postfix) with ESMTP id DD09013C447
	for <FreeBSD-gnats-submit@freebsd.org>; Sun, 30 Sep 2007 21:17:00 +0000 (UTC)
	(envelope-from blgl@stacken.kth.se)
Received: from igloo.stacken.kth.se (igloo.stacken.kth.se [130.237.234.40])
	by brev.stacken.kth.se (8.12.10/8.12.10) with ESMTP id l8UKtQhe015378
	for <FreeBSD-gnats-submit@freebsd.org>; Sun, 30 Sep 2007 22:55:26 +0200 (MET DST)
Received: from igloo.stacken.kth.se (localhost [127.0.0.1])
	by igloo.stacken.kth.se (8.13.3/8.13.3) with ESMTP id l8UKtQnb071625
	for <FreeBSD-gnats-submit@freebsd.org>; Sun, 30 Sep 2007 22:55:26 +0200 (CEST)
	(envelope-from blgl@igloo.stacken.kth.se)
Received: (from blgl@localhost)
	by igloo.stacken.kth.se (8.13.3/8.13.3/Submit) id l8UKtOUQ071624;
	Sun, 30 Sep 2007 22:55:24 +0200 (CEST)
	(envelope-from blgl)
Message-Id: <200709302055.l8UKtOUQ071624@igloo.stacken.kth.se>
Date: Sun, 30 Sep 2007 22:55:24 +0200 (CEST)
From: Bo Lindbergh <blgl@stacken.kth.se>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: Unfortunate fifo/O_NONBLOCK/kevent interaction
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         116770
>Category:       kern
>Synopsis:       [kqueue] Unfortunate fifo/O_NONBLOCK/kevent interaction
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Sun Sep 30 21:20:03 GMT 2007
>Closed-Date:    
>Last-Modified:  Mon Apr 14 19:58:02 UTC 2008
>Originator:     Bo Lindbergh <blgl@stacken.kth.se>
>Release:        FreeBSD 5.4-RELEASE-p6 i386
>Organization:
>Environment:
System: FreeBSD igloo.stacken.kth.se 5.4-RELEASE-p6 FreeBSD 5.4-RELEASE-p6 #1: Thu Jul 28 19:42:42 CEST 2005 root@igloo.stacken.kth.se:/usr/obj/usr/src/sys/IGLOO i386


>Description:
When a fifo with no writers is opened nonblockingly for reading and
the resulting file descriptor is added to a kqueue, kevent will
immediately report an EOF condition.  This is less than useful.

The useful behaviour would be for kevent not to report EOF until at
least one writer has come and gone.  This matches how similar
mechanisms in other operating systems work (e.g. epoll in Linux,
/dev/poll in Solaris).

>How-To-Repeat:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/event.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>

int main(void)
{
    int rfd,wfd,qfd,n;
    static struct timespec zero;
    struct kevent kev;
    char fifopath[64];

    sprintf(fifopath,"/tmp/less-than-useful.%ld",(long)getpid());
    if (mkfifo(fifopath,S_IRUSR|S_IWUSR)==-1) {
        perror("mkfifo");
        return 1;
    }
    rfd=open(fifopath,O_RDONLY|O_NONBLOCK);
    if (rfd==-1) {
        perror("open 1");
        goto cleanup;
    }
    qfd=kqueue();
    if (qfd==-1) {
        perror("kqueue");
        goto cleanup;
    }
    EV_SET(&kev,rfd,EVFILT_READ,EV_ADD,0,0,NULL);
    n=kevent(qfd,&kev,1,NULL,0,&zero);
    if (n==-1) {
        perror("kevent 1");
        goto cleanup;
    }
    n=kevent(qfd,NULL,0,&kev,1,&zero);
    if (n==-1) {
        perror("kevent 2");
        goto cleanup;
    }
    if (n==1 && (kev.flags & EV_EOF)) {
        fputs("Immediate EOF (not useful)\n",stderr);
    }
    wfd=open(fifopath,O_WRONLY);
    if (wfd==-1) {
        perror("open 2");
        goto cleanup;
    }
    n=kevent(qfd,NULL,0,&kev,1,&zero);
    if (n==-1) {
        perror("kevent 3");
        goto cleanup;
    }
    if (n==1 && (kev.flags & EV_EOF)) {
        fputs("EOF while there's still a writer (BUG!)\n",stderr);
    }
    close(wfd);
    n=kevent(qfd,NULL,0,&kev,1,&zero);
    if (n==-1) {
        perror("kevent 4");
        goto cleanup;
    }
    if (n==1 && (kev.flags & EV_EOF)) {
        fputs("EOF after the last writer went away (useful)\n",stderr);
    }
    close(qfd);
    close(rfd);
    unlink(fifopath);
    return 0;

 cleanup:
    unlink(fifopath);
    return 1;
}

>Fix:
Don't CANTRCVMORE the socketpair immediately after creating it.  Add
code to fifo_read_f to avoid calling soreceive blockingly when there
are zero writers.

Or just add a fi_seen_at_least_one_writer flag to struct fifoinfo...

>Release-Note:
>Audit-Trail:

From: Bruce Evans <brde@optusnet.com.au>
To: Bo Lindbergh <blgl@stacken.kth.se>
Cc: FreeBSD-gnats-submit@freebsd.org, freebsd-bugs@freebsd.org
Subject: Re: kern/116770: Unfortunate fifo/O_NONBLOCK/kevent interaction
Date: Mon, 1 Oct 2007 17:26:02 +1000 (EST)

 On Sun, 30 Sep 2007, Bo Lindbergh wrote:
 
 >> Description:
 > When a fifo with no writers is opened nonblockingly for reading and
 > the resulting file descriptor is added to a kqueue, kevent will
 > immediately report an EOF condition.  This is less than useful.
 
 But it is an EOF condition: in this state, read() must return 0 to
 indicate EOF.  select(), poll() and kqueue could have another mechanism
 for reporting this state, but don't in FreeBSD.  Some other OS's have
 a specially broken select() and/or poll() for fifos but not for other
 file types so that the polling read condition doesn't actually report
 the read condition for fifos only.
 
 >> Fix:
 > Don't CANTRCVMORE the socketpair immediately after creating it.  Add
 > code to fifo_read_f to avoid calling soreceive blockingly when there
 > are zero writers.
 
 That would break read().
 
 > Or just add a fi_seen_at_least_one_writer flag to struct fifoinfo...
 
 Fixing this, or even implementing bug for bug compatibility with other
 OS's, is not easy.  See PR's 34020, 53447, 76144, 76125, 76525, 94722
 and the resulting commits for previous attempts to fix this.
 
 PR 94722 does something like this.  It only tries to fix poll().  The
 behaviour of select() on a read descriptor cannot be changed, except
 to remove old buggy attempts to fix this problem, since select() on
 a read descriptor has no way to distinguish initial EOF from hangup.
 select() on an exceptional descriptor could consider hangup as an
 exception; there is no standard for this, but since exceptional
 descriptors are rarely used, changing the behaviour for them wouldn't
 break much.  Kqueue has flags so it should be able to use the fix for
 poll() fairly easily.
 
 Bruce
>Unformatted:
