0

I am trying to write a "cleaner" program to release a potential writer which is blocked at a named pipe (because no reader is reading from the pipe). However, the cleaner itself should not block when no writer is blocked writing to the pipe. In other words, the "cleaner" must return/terminate immediately, whether there is a blocked writer or not.

Therefore I searched for "Python non-blocking read from named pipe", and got these:

  1. How to read named FIFO non-blockingly?
  2. fifo - reading in a loop
  3. What conditions result in an opened, nonblocking named pipe (fifo) being "unavailable" for reads?
  4. Why does a read-only open of a named pipe block?

It seems that they suggest simply using os.open(file_name, os.O_RDONLY | os.O_NONBLOCK) should be fine, which didn't really work on my machine. I think I may have messed up somewhere or misunderstood some of their suggestion/situation. However, I really couldn't figure out what's wrong myself.

I found Linux man page (http://man7.org/linux/man-pages/man2/open.2.html), and the explanation of O_NONBLOCK seems consistent with their suggestions but not with my observation on my machine...

Just in case it is related, my OS is Ubuntu 14.04 LTS 64-bit.

Here is my code:

import os
import errno

BUFFER_SIZE = 65536

ph = None
try:
    ph = os.open("pipe.fifo", os.O_RDONLY | os.O_NONBLOCK)
    os.read(ph, BUFFER_SIZE)
except OSError as err:
    if err.errno == errno.EAGAIN or err.errno == errno.EWOULDBLOCK:
        raise err
    else:
        raise err
finally:
    if ph:
        os.close(ph)

(Don't know how to do Python syntax highlighting...)

Originally there is only the second raise, but I found that os.open and os.read, though not blocking, don't raise any exception either... I don't really know how much the writer will write to the buffer! If the non blocking read does not raise exception, how should I know when to stop reading?


Updated on 8/8/2016:

This seems to be a workaround/solution that satisfied my need:

import os
import errno

BUFFER_SIZE = 65536

ph = None
try:
    ph = os.open("pipe.fifo", os.O_RDONLY | os.O_NONBLOCK)
    while True:
        buffer = os.read(ph, BUFFER_SIZE)
        if len(buffer) < BUFFER_SIZE:
            break
except OSError as err:
    if err.errno == errno.EAGAIN or err.errno == errno.EWOULDBLOCK:
        pass # It is supposed to raise one of these exceptions
    else:
        raise err
finally:
    if ph:
        os.close(ph)

It will loop on read. Every time it reads something, it compares the size of the content read with the specified BUFFER_SIZE, until it reaches EOF (writer will then unblock and continue/exit).

I still want to know why no exception is raised in that read.


Updated on 8/10/2016:

To make it clear, my overall goal is like this.

My main program (Python) has a thread serving as the reader. It normally blocks on the named pipe, waiting for "commands". There is a writer program (Shell script) which will write a one-liner "command" to the same pipe in each run.

In some cases, a writer starts before my main program starts, or after my main program terminates. In this case, the writer will block on the pipe waiting for a reader. In this way, if later my main program starts, it will read immediately from the pipe to get that "command" from the blocked writer - this is NOT what I want. I want my program to disregard writers that started before it.

Therefore, my solution is, during initialization of my reader thread, I do non-blocking read to release the writers, without really executing the "command" they were trying to write to the pipe.

Community
  • 1
  • 1
bfrguci
  • 137
  • 2
  • 8

2 Answers2

0

This solution is incorrect.

while True:
    buffer = os.read(ph, BUFFER_SIZE)
    if len(buffer) < BUFFER_SIZE:
        break

This will not actually read everything, it will only read until it gets a partial read. Remember: You are only guaranteed to fill the buffer with regular files, in all other cases it is possible to get a partial buffer before EOF. The correct way to do this is to loop until the actual end of file is reached, which will give a read of length 0. The end of file indicates that there are no writers (they have all exited or closed the fifo).

while True:
    buffer = os.read(ph, BUFFER_SIZE)
    if not buffer:
        break

However, this will not work correctly in the face of non-blocking IO. It turns out non-blocking IO is completely unnecessary here.

import os
import fcntl

h = os.open("pipe.fifo", os.O_RDONLY | os.O_NONBLOCK)
# Now that we have successfully opened it without blocking,
# we no longer want the handle to be non-blocking
flags = fcntl.fcntl(h, fcntl.F_GETFL)
flags &= ~os.O_NONBLOCK
fcntl.fcntl(h, fcntl.F_SETFL, flags)
try:
    while True:
        # Only blocks if there is a writer
        buf = os.read(h, 65536)
        if not buf:
            # This happens when there are no writers
            break
finally:
    os.close(h)

The only scenario which will cause this code to block is if there is an active writer which has opened the fifo but is not writing to it. From what you've described, it doesn't sound like this is the case.

Non-blocking IO doesn't do that

Your program wants to do two things, depending on circumstance:

  1. If there are no writers, return immediately.

  2. If there are writers, read data from the FIFO until the writers are done.

Non-blocking read() has no effect whatsoever on task #1. Whether you use O_NONBLOCK or not, read() will return immediately in situation #1. So the only difference is in situation #2.

In situation #2, your program's goal is to read the entire block of data from the writers. That is exactly how blocking IO works: it waits for the writers to finish, and then read() returns. The whole point of non-blocking IO is to return early if the operation can't complete immediately, which is the opposite of your program's goal—which is to wait until the operation is complete.

If you use non-blocking read(), in situation #2, your program will sometimes return early, before the writers have finished their jobs. Or maybe your program will return after reading half of a command from the FIFO, leaving the other (now corrupted) half there. This concern is expressed in your question:

If the non blocking read does not raise exception, how should I know when to stop reading?

You know when to stop reading because read() returns zero bytes when all writers have closed the pipe. (Conveniently, this is also what happens if there were no writers in the first place.) This is unfortunately not what happens if the writers do not close their end of the pipe when they are done. It is far simpler and more straightforward if the writers close the pipe when done, so this is the recommended solution, even if you need to modify the writers a little bit. If the writers cannot close the pipe for whatever reason, the solution is more complicated.

The main use case for non-blocking read() is if your program has some other task to complete while IO goes on in the background.

Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • I don't understand what you are doing in the last section of codes. If you remove the O_NONBLOCK flag, won't it block on `read`? – bfrguci Aug 09 '16 at 21:39
  • I thought you wanted to read all of the data from the pipe, until no writer is active? – Dietrich Epp Aug 09 '16 at 22:52
  • Yes, and at the same time, make sure the reader itself does not block. Actually the second part is even more important, because it would be rare in my case to have multiple writers blocked at the pipe. – bfrguci Aug 09 '16 at 23:50
  • Right. Suppose you have one writer blocked. The reader calls `read()` until that *one* writer closes the pipe. Is that not what you want? – Dietrich Epp Aug 10 '16 at 13:58
  • If there is no writer blocked, the reader should not block - it should exit immediately when I run it. Does your code work this way? – bfrguci Aug 10 '16 at 17:21
  • What exactly are you trying to accomplish here? To clarify: what are the larger goals here? In general, a writer will continue to be blocked until *all* its output is consumed, not just one chunk. So if you want to actually unstick a writer, you have to have some way of knowing when the writer is unstuck, and the normal way to do this is to read until the writer signals it is done writing by closing its file handle. How else would you know that the writer is unstuck? – Dietrich Epp Aug 10 '16 at 17:25
  • @bfrguci: Yes, that is exactly what this solves. You *open* in non-blocking mode, in case there are no writers, so you can return immediately if there are no writers. However, you *read* in blocking mode, so you can drain all of the data from the writer that is already there. This only assumes that the writer will close the handle when it is done, which is exactly what happens if you do something like `echo x > ./my_fifo`. – Dietrich Epp Aug 10 '16 at 18:06
  • If my open is non-blocking, I will open it whether or not there is a writer. In this way, how do I know if I should start reading in blocking mode? – bfrguci Aug 10 '16 at 18:40
  • @bfrguci: You're right, I misunderstood how non-blocking works. The correct way to do this is to just open the file in blocking mode and read until empty. If there are no writers, then it will immediately return EOF and not block at all. – Dietrich Epp Aug 10 '16 at 18:57
  • "The correct way to do this is to just **open the file in blocking mode** and read until empty." Excuse me? I think I should open in non-blocking mode, otherwise it will be blocked if not writer is there... – bfrguci Aug 10 '16 at 19:16
  • @bfrguci: No, it will not be blocked. That's not how reading from FIFOs works. – Dietrich Epp Aug 10 '16 at 19:40
  • @bfrguci: This would be a lot easier if you actually tried running some of the code. You would see that `os.read()` returns `b''` if there are no writers. I ran sample code while I was writing the answer, and it made it a lot easier to make sure I was getting things right. – Dietrich Epp Aug 10 '16 at 19:58
  • According to what I see from my tests, `os.open("pipe.fifo", os.O_RDONLY)` WILL BLOCK if you don't specify O_NONBLOCK... Did you really try this? – bfrguci Aug 12 '16 at 01:14
  • @bfrguci: Okay, then open it non-blocking and change it back to blocking if successful. I am still a bit confused as to why this is so hard. – Dietrich Epp Aug 12 '16 at 01:16
  • And again, please try running `os.read()` and seeing that it *won't block* if there are no writers. I feel like I am just saying this point over and over again and it feels like talking to a wall. – Dietrich Epp Aug 12 '16 at 01:23
  • The final version (opening nonblocking and reading blocking) works well. However, please be responsible on your words - It was your answer being unclear which prevents me from getting your point. Also, your code in the current answer now is still unclear because it opens the pipe blocking (`h = os.open("pipe.fifo", os.O_RDONLY)`), which definitely won't work. And you claimed nonblocking I/O is not necessary, which is not true. – bfrguci Aug 16 '16 at 17:16
  • If you refine your answer to the latest version you gave in comments, it would be good. Note, you didn't answer the question I asked in the subject: it is not raising exception where it is expected to according to answers I found on web. – bfrguci Aug 16 '16 at 17:17
  • Fixed typo with missing `O_NONBLOCK`. The answer was scrubbed references to non-blocking IO, since that is ambiguous. The `os.open` should be non-blocking (since you don't want to wait for a writer if there are no writers) but the `os.read` should be blocking (since you want to wait for a writer to finish if a writer exists). So the phrase "non-blocking `read()`" is used instead of "non-blocking IO", to make it clear that only `read()` is non-blocking, not `open()`. Note that of course `os.open()` will not raise an exception, that behavior is documented in `man 2 open` (see "ERRORS"). – Dietrich Epp Aug 16 '16 at 18:21
0

In POSIX C programs, if read() attempts to read from an empty pipe or a FIFO special file, it has one of the following results:

  • If no process has the pipe open for writing, read() returns 0 to indicate the end of the file.
  • If some process has the pipe open for writing and O_NONBLOCK is set to 1, read() returns -1 and sets errno to EAGAIN.
  • If some process has the pipe open for writing and O_NONBLOCK is set to 0, read() blocks (that is, does not return) until some data is written, or the pipe is closed by all other processes that have the pipe open for writing.

So,first check if there's any writer still open the fifo for write. If there's no one, the read will get an empty string and no exception. Otherwise, an exception will be raised