Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect when reader closes named pipe (FIFO)

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.

like image 544
Jonathon Reinhart Avatar asked Feb 04 '15 07:02

Jonathon Reinhart


People also ask

What happens to a process that calls read () on an empty pipe when the writing end is open?

If a process attempts to read from an empty pipe, then read(2) will block until data is available.

What does mkfifo command do?

The mkfifo command creates FIFO special files specified by the File parameter, in the order specified.


3 Answers

If you are using an event loop that is based on the poll system call you can register the pipe with an event mask that contains EPOLLERR. In Python, with select.poll,

import select
fd = open("pipe", "w")
poller = select.poll()
poller.register(fd, select.POLLERR)
poller.poll()

will wait until the pipe is closed.

To test this, run mkfifo pipe, start the script, and in another terminal run, for example, cat pipe. As soon as you quit the cat process, the script will terminate.

like image 144
Phillip Avatar answered Oct 05 '22 19:10

Phillip


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)
like image 30
Jonathon Reinhart Avatar answered Oct 05 '22 18:10

Jonathon Reinhart


There is no such mechanism. Generally, according to the UNIX-way, there are no signals for streams opening or closing, on either end. This can only be detected by reading or writing to them (accordingly).

I would say this is wrong design. Currently you are trying to have the receiver signal their availability to receive by opening a pipe. So either you implement this signaling in an appropriate way, or incorporate the "closing logic" in the sending part of the pipe.

like image 33
Prikso NAI Avatar answered Oct 05 '22 17:10

Prikso NAI