I was trying to understand FIFOs using Python under linux and I found a strange behavior i don't understand.
The following is fifoserver.py
import sys
import time
def readline(f):
s = f.readline()
while s == "":
time.sleep(0.0001)
s = f.readline()
return s
while True:
f = open(sys.argv[1], "r")
x = float(readline(f))
g = open(sys.argv[2], "w")
g.write(str(x**2) + "\n")
g.close()
f.close()
sys.stdout.write("Processed " + repr(x) + "\n")
and this is fifoclient.py
import sys
import time
def readline(f):
s = f.readline()
while s == "":
time.sleep(0.0001)
s = f.readline()
return s
def req(x):
f = open("input", "w")
f.write(str(x) + "\n")
f.flush()
g = open("output", "r")
result = float(readline(g))
g.close()
f.close()
return result
for i in range(100000):
sys.stdout.write("%i, %s\n" % (i, i*i == req(i)))
I also created two FIFOs using mkfifo input and mkfifo output.
What I don't understand is why when I run the server (with python fifoserver.py input output) and the client (with python fifoclient.py) from two consoles after some requests the client crashes with a "broken pipe" error on f.flush(). Note that before crashing I've seen from a few hundreds to several thousands correctly processed requests running fine.
What is the problem in my code?
2 Answers 2
As other comments have alluded to, you have a race condition.
I suspect that in the failing case, the server gets suspended after one of these lines:
g.write(str(x**2) + "\n")
g.close()
The client is then able to read the result, print it to the screen, and loop back. It then reopens f - which succeeds, because it's still open on the server side - and writes the message. Meanwhile, the server has managed to close f. Next, the flush on the client side executes a write() syscall on the pipe, which triggers the SIGPIPE because it's now closed on the other side.
If I'm correct, you should be able to fix it by moving the server's f.close() to be above the g.write(...).
2 Comments
open/write/close sequence from a client would have stalled if the server only did open/read and didn't closed yet (so basically the server was in the same "session" of the first open). This behavior makes IMO much harder to use two FIFOs for bidirectional communication.I am not a unix expert, but my guess is that you eventually end up with the file closed in both processes, and the open-for-write happens next. As there is nothing to accept the data, the pipe breaks.
I don't understand why you are opening and closing the pipe all the time.
Try starting the process that reads the pipe first, have it open the pipe and it will sit waiting for data.
Then start the pipe-writer, and have it pump out all the data you want to send. It will stall if it gets ahead. When the writer closes the pipe, the reader gets zero bytes instead of blocking, and should close. IIRC, Python detects this and returns EOF.
3 Comments
flush call and opened the output FIFO for reading. Am I missing something?Explore related questions
See similar questions with these tags.
sys.argv[2]and never closing it. Don't assume the garbage collector will take care of that for you -- clean it up explicitly with a call toclose(), or better yet, use awithstatement.f = open(...)uses the nameffor a new file object, thereby dropping the last reference to the old one. The old file object gets garbage collected immediately, which includes that it is closed.