Is there any way for a writer to know that a reader has closed its end of a named pipe (or exited), without writing to it?
I need to know this because the initial data I write to the pipe is different; the reader is expecting an initial header before the rest of the data comes.
Currently, I detect this when my write()
fails with EPIPE
. I then set a flag that says “next time, send the header”. However, it is possible for the reader to close and re-open the pipe before I’ve written anything. In this case, I never realize what he’s done, and don’t send the header he is expecting.
Is there any sort of async event type thing that might help here? I’m not seeing any signals being sent.
Note that I haven’t included any language tags, because this question should be considered language-agnostic. My code is Python, but the answers should apply to C, or any other language with system call-level bindings.
Advertisement
Answer
Oddly enough, it appears that when the last reader closes the pipe, select
indicates that the pipe is readable:
writer.py
#!/usr/bin/env python import os import select import time NAME = 'fifo2' os.mkfifo(NAME) def select_test(fd, r=True, w=True, x=True): rset = [fd] if r else [] wset = [fd] if w else [] xset = [fd] if x else [] t0 = time.time() r,w,x = select.select(rset, wset, xset) print 'After {0} sec:'.format(time.time() - t0) if fd in r: print ' {0} is readable'.format(fd) if fd in w: print ' {0} is writable'.format(fd) if fd in x: print ' {0} is exceptional'.format(fd) try: fd = os.open(NAME, os.O_WRONLY) print '{0} opened for writing'.format(NAME) print 'select 1' select_test(fd) os.write(fd, 'test') print 'wrote data' print 'select 2' select_test(fd) print 'select 3 (no write)' select_test(fd, w=False) finally: os.unlink(NAME)
Demo:
Terminal 1:
$ ./pipe_example_simple.py fifo2 opened for writing select 1 After 1.59740447998e-05 sec: 3 is writable wrote data select 2 After 2.86102294922e-06 sec: 3 is writable select 3 (no write) After 2.15910816193 sec: 3 is readable
Terminal 2:
$ cat fifo2 test # (wait a sec, then Ctrl+C)