Reputation: 59436
My overall goal is to pass report information into a named pipe if (and only if) there is a reader connected to that named pipe. If there is no reader, I want to avoid building the report information etc. Since this is happening in a scenario where other data streams are handled via select()
, I want to add the named pipe to the streams which are "ready for writing".
So, I want to open a named pipe for writing and need to give it to select()
in a way so that select()
only returns in case there really is a reader on the other end of the named pipe.
Normally, if there is no reader, the open
call already hangs; unfortunately, I cannot give an open
action to select()
—only open file descriptors are valid.
To get a decent file descriptor, I'm currently creating a dummy reader (I open the named pipe for reading myself), then open it for writing, and then close the dummy reader again:
dummy = os.open(fifoPath, os.O_RDONLY | os.O_NONBLOCK)
# ^^^ without the NONBLOCK, this will hang until another process is ready to write
fifo = os.open(fifoPath os.O_WRONLY)
os.close(dummy)
If I now open another reader, he will receive what I write into the named pipe, so this aspect is working.
But now select()
always returns the fifo
as ready-to-write, even if there is no reader connected:
r, w, e = select.select([], [ fifo ], [])
print w[0]
This will always and at once print the fifo
value.
And writing into the named pipe also does not hang, no matter how much I write:
fifo.write('foo')
The data I write just is lost as no reader is connected.
When opening my dummy reader, I am passing the flag O_NONBLOCK
because otherwise this opening hangs as well. I tried various methods but could not figure out how to do this properly.
Can anybody tell me how to combine writing into named pipes with select()
?
(The code above is in Python but I guess the problem is not really connected to Python but rather a Linux/Unix problem, so I won't tag this question as Python.)
Upvotes: 4
Views: 3696
Reputation: 11546
See man 7 fifo
WRT how the pipe should work non-block.
A process can open a FIFO in nonblocking mode. In this case, opening for read-only will succeed even if no-one has opened on the write side yet, opening for write-only will fail with ENXIO (no such device or address) unless the other end has already been opened.
Under Linux, opening a FIFO for read and write will succeed both in blocking and nonblocking mode. This can be used to open a FIFO for writing while there are no readers available. A process that uses both ends of the connection in order to communicate with itself should be very careful to avoid deadlocks.
That should get you around the dummy
hack. Using the fd with select()
, however, is I think a non-starter. You could instead use a short timeout with select()
to facilitate polling the fd with write()
at intervals inside your main loop, exploiting the fact that...
When a process tries to write to a FIFO that is not opened for read on the other side, the process is sent a SIGPIPE signal.
...by setting a global to true
before the call and false
inside a SIGPIPE handler to indicate there's no reader.
Python may be an issue here, though, since you should be getting this signal already but apparently are not. It could be because of the dummy
hack (you really have to get rid of that).
Another, perhaps better idea, is to fork a handler for the fifo that uses blocking calls, but also maintains a normal pair (read/write) of pipe descriptors to the main process. One of those (the writer; i.e., read by the main process) can be used with select()
. The fork waits on open()
and signals main when a reader connects.
Beware that once you reach EOF because the reader disconnected, you must re-open the pipe. Do not continue to use the same fd.
If at all feasible I'd opt for a unix local socket over a fifo.
Upvotes: 2