Bug 248440

Summary: kqueue(2): kevent not returning full pipes registered with EVFILT_WRITE on read end closure
Product: Base System Reporter: larkinconr
Component: kernAssignee: freebsd-bugs (Nobody) <bugs>
Status: Closed FIXED    
Severity: Affects Some People CC: kevans, markj
Priority: --- Keywords: needs-qa
Version: 12.1-RELEASEFlags: koobs: maintainer-feedback? (markj)
Hardware: Any   
OS: Any   
See Also: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=203366
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=224615
https://reviews.freebsd.org/D24528
Attachments:
Description Flags
test program to reproduce bug via filling/monitoring stdout none

Description larkinconr 2020-08-02 23:20:02 UTC
Created attachment 216970 [details]
test program to reproduce bug via filling/monitoring stdout

When using kevent to monitor the write end of a pipe for writability via EVFILT_WRITE, if the pipe is full when the read end is closed, an event is not returned. I would expect an event with EV_EOF set to be returned. Note that this does not seem to reproduce with sockets or named pipes (fifos), those return an event with EV_EOF set just fine in this situation.

I'm attaching a sample program that verifies stdout is a pipe and then attempts to fill  it with data, then waits for an event with EVFILT_WRITE for stdout. If you pipe this to  some program that doesn't read stdin, for instance sleep(1), the pipeline hangs forever. Notably, if you instead redirect its output to a named pipe and have sleep "read" the named pipe with shell redirection, the program *does* receive an event for stdout with EV_EOF set once sleep exits.

I originally detected this with a test case that creates a pipe via pipe(2) in process, so testing with socketpair(2) was convenient, but it used threads and other infrastructure etc so I wrote a new test program. But I just want to mention that because it would seem to exclude some weird interaction with my shell etc.

Example test case using the attached kqwrite.c:

$ ./kqwrite | /bin/sleep 1 # hangs forever
^C
$ mkfifo dummy; /bin/sleep 1 < dummy &; ./kqwrite > dummy # returns EV_EOF:
[1] 1734
kevent returned 1
        1 -2 32768 0 0 0x0
[1]  + done       /bin/sleep 1 < dummy
Comment 1 Kubilay Kocak freebsd_committer freebsd_triage 2020-08-02 23:37:06 UTC
^Triage: Request feedback from markj per base r360380 base r360379 base r360378, which may be related (or resolve) this issue.

@Mark If this is indeed resolved, could you please close this a duplicate of the relevant issue ID (if this report matches a previously reported one)
Comment 2 Mark Johnston freebsd_committer freebsd_triage 2020-08-04 16:11:35 UTC
Indeed, I believe this is fixed in head and on stable/12 (so it will be fixed in 12.2).  The test case does not hang for me on head:

$ ./kqwrite | sleep 1
kevent returned 1
        1 -2 32768 0 0 0x0
$
Comment 3 Mark Johnston freebsd_committer freebsd_triage 2020-08-04 16:14:20 UTC
It should be r360379 that fixed this.  The referenced PRs have to do with named pipes, which are not related to this particular report, so I'll just mark the bug fixed without duping.  Please feel free to comment further or re-open if necessary.