Skip to content

Commit

Permalink
[3.12] gh-120012: clarify the behaviour of `multiprocessing.Queue.emp…
Browse files Browse the repository at this point in the history
…ty` on closed queues. (GH-120102) (#120470)

Co-authored-by: Bénédikt Tran <[email protected]>
Co-authored-by: Gregory P. Smith <[email protected]>
  • Loading branch information
3 people authored Jul 14, 2024
1 parent 98340a0 commit 863c0a3
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Doc/library/multiprocessing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,8 @@ For an example of the usage of queues for interprocess communication see
Return ``True`` if the queue is empty, ``False`` otherwise. Because of
multithreading/multiprocessing semantics, this is not reliable.

May raise an :exc:`OSError` on closed queues. (not guaranteed)

.. method:: full()

Return ``True`` if the queue is full, ``False`` otherwise. Because of
Expand Down Expand Up @@ -950,6 +952,8 @@ For an example of the usage of queues for interprocess communication see

Return ``True`` if the queue is empty, ``False`` otherwise.

Always raises an :exc:`OSError` if the SimpleQueue is closed.

.. method:: get()

Remove and return an item from the queue.
Expand Down
26 changes: 26 additions & 0 deletions Lib/test/_test_multiprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1332,6 +1332,23 @@ def _on_queue_feeder_error(e, obj):
self.assertTrue(not_serializable_obj.reduce_was_called)
self.assertTrue(not_serializable_obj.on_queue_feeder_error_was_called)

def test_closed_queue_empty_exceptions(self):
# Assert that checking the emptiness of an unused closed queue
# does not raise an OSError. The rationale is that q.close() is
# a no-op upon construction and becomes effective once the queue
# has been used (e.g., by calling q.put()).
for q in multiprocessing.Queue(), multiprocessing.JoinableQueue():
q.close() # this is a no-op since the feeder thread is None
q.join_thread() # this is also a no-op
self.assertTrue(q.empty())

for q in multiprocessing.Queue(), multiprocessing.JoinableQueue():
q.put('foo') # make sure that the queue is 'used'
q.close() # close the feeder thread
q.join_thread() # make sure to join the feeder thread
with self.assertRaisesRegex(OSError, 'is closed'):
q.empty()

def test_closed_queue_put_get_exceptions(self):
for q in multiprocessing.Queue(), multiprocessing.JoinableQueue():
q.close()
Expand Down Expand Up @@ -5691,6 +5708,15 @@ def _test_empty(cls, queue, child_can_start, parent_can_continue):
finally:
parent_can_continue.set()

def test_empty_exceptions(self):
# Assert that checking emptiness of a closed queue raises
# an OSError, independently of whether the queue was used
# or not. This differs from Queue and JoinableQueue.
q = multiprocessing.SimpleQueue()
q.close() # close the pipe
with self.assertRaisesRegex(OSError, 'is closed'):
q.empty()

def test_empty(self):
queue = multiprocessing.SimpleQueue()
child_can_start = multiprocessing.Event()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Clarify the behaviours of :meth:`multiprocessing.Queue.empty` and
:meth:`multiprocessing.SimpleQueue.empty` on closed queues.
Patch by Bénédikt Tran.

0 comments on commit 863c0a3

Please sign in to comment.