TL;DR: Why does closing a fifo file (named pipe) that received a SIGPIPE exception generate another SIGPIPE exception?
My python script is writing bytes to another process, which is a subprocess of my python process, through a FIFO file. (There are some restrictions that I must use a named pipe.)
I have to take account the fact that the subprocess might terminate prematurely. If that happens, my python script must reap the dead subprocess and start it again.
To see whether the subprocess dies, I simply try to write to the FIFO first, and if I get a SIGPIPE exception (actually IOError indicating broken pipe), I know it is time to restart my subprocess.
The minimum example goes as follows:
#!/usr/bin/env python3
import os
import signal
import subprocess
# The FIFO file.
os.mkfifo('tmp.fifo')
# A subprocess to simply discard any input from the FIFO.
FNULL = open(os.devnull, 'w')
proc = subprocess.Popen(['/bin/cat', 'tmp.fifo'], stdout=FNULL, stderr=FNULL)
print('pid = %d' % proc.pid)
# Open the FIFO, and MUST BE BINARY MODE.
fifo = open('tmp.fifo', 'wb')
# Endlessly write to the FIFO.
while True:
# Try to write to the FIFO, restart the subprocess on demand, until succeeded.
while True:
try:
# Optimistically write to the FIFO.
fifo.write(b'hello')
except IOError as e:
# The subprocess died. Close the FIFO and reap the subprocess.
fifo.close()
os.kill(proc.pid, signal.SIGKILL)
proc.wait()
# Start the subprocess again.
proc = subprocess.Popen(['/bin/cat', 'tmp.fifo'], stdout=FNULL, stderr=FNULL)
print('pid = %d' % proc.pid)
fifo = open('tmp.fifo', 'wb')
else:
# The write goes on well.
break
To reproduce the result, run that script and manually kill the subprocess by kill -9 <pid>
. The traceback will tell that
Traceback (most recent call last):
File "./test.py", line 24, in <module>
fifo.write(b'hello')
BrokenPipeError: [Errno 32] Broken pipe
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "./test.py", line 27, in <module>
fifo.close()
BrokenPipeError: [Errno 32] Broken pipe
So why does closing the FIFO file generate another SIGPIPE exception?
I ran the test on the following platforms and the results are same.
Python 3.7.6 @ Darwin Kernel Version 19.3.0 (MacOS 10.15.3)
Python 3.6.8 @ Linux 4.18.0-147.3.1.el8_1.x86_64 (Centos 8)