You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Fix a BrokenPipeError when a multiprocessing.Queue is garbage collected #91185
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee=Noneclosed_at=Nonecreated_at=<Date2022-03-15.18:28:33.219>labels= ['3.10', 'library', '3.9', 'type-crash', '3.11']
title='Fix a BrokenPipeError when a multiprocessing.Queue is garbage collected'updated_at=<Date2022-03-16.16:27:25.124>user='https://github.com/maggyero'
A BrokenPipeError exception is raised when the queue thread of a multiprocessing.Queue still sends enqueued items to the write end of the queue pipe after the read end of the queue pipe has been automatically closed during its garbage collection following the garbage collection of the queue (the write end of the queue pipe is not garbage collected because it is also referenced by the queue thread):
I have attached the following patch: pass a reference to the reader end of the queue pipe to the queue thread so that the reader end is not garbage collected and closed before the queue thread has sent all the buffered data to the writer end.
maggyero commented on Mar 16, 2022
maggyeromannequin
MannequinAuthor
More actions
I forgot to include the output of the above program:
Traceback (most recent call last):
File "/usr/local/Cellar/python@3.9/3.9.10/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/queues.py", line 251, in _feed
send_bytes(obj)
File "/usr/local/Cellar/python@3.9/3.9.10/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/connection.py", line 205, in send_bytes
self._send_bytes(m[offset:offset + size])
File "/usr/local/Cellar/python@3.9/3.9.10/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/connection.py", line 416, in _send_bytes
self._send(header + buf)
File "/usr/local/Cellar/python@3.9/3.9.10/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/connection.py", line 373, in _send
n = write(self._handle, buf)
BrokenPipeError: [Errno 32] Broken pipe
I am seeing this same issue and it's causing my unit tests to print a bunch of spurious BrokenPipeError chatter. It's subject to race conditions, and the sample code in #91185 (comment) only manifests the issue a fraction of the time on my development machine. If I want to reliably reproduce the BrokenPipeError, this main() does it:
defmain():
q=multiprocessing.Queue()
q.put(0)
#time.sleep(0.001) # Issue goes away when this line is uncommentedq.close()
q.join_thread()
If I uncomment 1ms delay, the BrokenPipe error goes away.
The PR from @maggyero didn't fix this issue for me with my modified main().
I believe the root cause of this bug is that Queue.close() closes self._reader, but that may be premature if the current process is the only one holding a reference to that reader and there are still objects in self._buffer that the _feed thread hasn't flushed out to self._writer.
I wouldn't consider this a real fix since the reader is surely being closed explicitly for a reason (and because this is ugly code), but as a proof of concept, this change eliminates the BrokenPipeError reliably for me:
I tried this pull request and didn't fix the problem for me. Might you be getting tricked by how racy this issue is into thinking that an unrelated change is resolving it?
Thanks for the code sample @malsyned. I have updated the PR and it fixes your issue too. The problem in your case was that Queue.close explicitly closed the read end self._reader of the queue pipe from the main thread before notifying the queue thread to stop flushing the buffer self._buffer to the write end self._writer of the queue pipe. So I solved this by closing the read end self._reader of the queue pipe from the queue thread instead.
If all file descriptors referring to the write end of a pipe have been closed, then an attempt to read(2) from the pipe will see end-of-file (read(2) will return 0). If all file descriptors referring to the read end of a pipe have been closed, then a write(2) will cause a SIGPIPE signal to be generated for the calling process. If the calling process is ignoring this signal, then write(2) fails with the error EPIPE. An application that uses pipe(2) and fork(2) should use suitable close(2) calls to close unnecessary duplicate file descriptors; this ensures that end-of-file and SIGPIPE/EPIPE are delivered when appropriate.
I am hitting a similar issue in Python 3.8 when using mp.Queues from asyncio.run-in_executor.
Is there a workaround or fix or I have to upgrade Python?
Activity
maggyero commentedon Mar 15, 2022
A
BrokenPipeErrorexception is raised when the queue thread of amultiprocessing.Queuestill sends enqueued items to the write end of the queue pipe after the read end of the queue pipe has been automatically closed during its garbage collection following the garbage collection of the queue (the write end of the queue pipe is not garbage collected because it is also referenced by the queue thread):maggyero commentedon Mar 15, 2022
I have attached the following patch: pass a reference to the reader end of the queue pipe to the queue thread so that the reader end is not garbage collected and closed before the queue thread has sent all the buffered data to the writer end.
maggyero commentedon Mar 16, 2022
I forgot to include the output of the above program:
malsyned commentedon Apr 13, 2022
I am seeing this same issue and it's causing my unit tests to print a bunch of spurious BrokenPipeError chatter. It's subject to race conditions, and the sample code in #91185 (comment) only manifests the issue a fraction of the time on my development machine. If I want to reliably reproduce the BrokenPipeError, this
main()does it:If I uncomment 1ms delay, the BrokenPipe error goes away.
malsyned commentedon Apr 13, 2022
The PR from @maggyero didn't fix this issue for me with my modified
main().I believe the root cause of this bug is that
Queue.close()closesself._reader, but that may be premature if the current process is the only one holding a reference to that reader and there are still objects inself._bufferthat the_feedthread hasn't flushed out toself._writer.I wouldn't consider this a real fix since the reader is surely being closed explicitly for a reason (and because this is ugly code), but as a proof of concept, this change eliminates the BrokenPipeError reliably for me:
malsyned commentedon Apr 13, 2022
Some further evidence: putting this code before
q.close()also stops mymain()from reproducing this issue:[edit: mostly stops. There's an additional race condition that happens very rarely as well]
malsyned commentedon Apr 14, 2022
Is this a dupe of #80025?
geryogam commentedon Apr 26, 2022
Thanks for the code sample @malsyned. I have updated the PR and it fixes your issue too. The problem in your case was that
Queue.closeexplicitly closed the read endself._readerof the queue pipe from the main thread before notifying the queue thread to stop flushing the bufferself._bufferto the write endself._writerof the queue pipe. So I solved this by closing the read endself._readerof the queue pipe from the queue thread instead.From the Linux manual page of pipe:
geryogam commentedon May 11, 2022
Hi @JelleZijlstra. Could you close this issue now that it has been fixed in PR #31913? (I cannot do it from my account.)
geryogam commentedon May 11, 2022
@JelleZijlstra Thanks!
gatopeich commentedon Feb 11, 2023
I am hitting a similar issue in Python 3.8 when using mp.Queues from asyncio.run-in_executor.
Is there a workaround or fix or I have to upgrade Python?
geryogam commentedon Feb 11, 2023
@gatopeich Could you open a separate issue with a minimal working example to reproduce the bug?