mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
update queue to 3.13.3
This commit is contained in:
2 files changed
+475
-1
No files matched your search
Vendored
+59
-1
@@ -10,7 +10,15 @@ try:
|
||||
except ImportError:
|
||||
SimpleQueue = None
|
||||
|
||||
__all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue', 'SimpleQueue']
|
||||
__all__ = [
|
||||
'Empty',
|
||||
'Full',
|
||||
'ShutDown',
|
||||
'Queue',
|
||||
'PriorityQueue',
|
||||
'LifoQueue',
|
||||
'SimpleQueue',
|
||||
]
|
||||
|
||||
|
||||
try:
|
||||
@@ -25,6 +33,10 @@ class Full(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ShutDown(Exception):
|
||||
'''Raised when put/get with shut-down queue.'''
|
||||
|
||||
|
||||
class Queue:
|
||||
'''Create a queue object with a given maximum size.
|
||||
|
||||
@@ -54,6 +66,9 @@ class Queue:
|
||||
self.all_tasks_done = threading.Condition(self.mutex)
|
||||
self.unfinished_tasks = 0
|
||||
|
||||
# Queue shutdown state
|
||||
self.is_shutdown = False
|
||||
|
||||
def task_done(self):
|
||||
'''Indicate that a formerly enqueued task is complete.
|
||||
|
||||
@@ -65,6 +80,9 @@ class Queue:
|
||||
have been processed (meaning that a task_done() call was received
|
||||
for every item that had been put() into the queue).
|
||||
|
||||
shutdown(immediate=True) calls task_done() for each remaining item in
|
||||
the queue.
|
||||
|
||||
Raises a ValueError if called more times than there were items
|
||||
placed in the queue.
|
||||
'''
|
||||
@@ -129,8 +147,12 @@ class Queue:
|
||||
Otherwise ('block' is false), put an item on the queue if a free slot
|
||||
is immediately available, else raise the Full exception ('timeout'
|
||||
is ignored in that case).
|
||||
|
||||
Raises ShutDown if the queue has been shut down.
|
||||
'''
|
||||
with self.not_full:
|
||||
if self.is_shutdown:
|
||||
raise ShutDown
|
||||
if self.maxsize > 0:
|
||||
if not block:
|
||||
if self._qsize() >= self.maxsize:
|
||||
@@ -138,6 +160,8 @@ class Queue:
|
||||
elif timeout is None:
|
||||
while self._qsize() >= self.maxsize:
|
||||
self.not_full.wait()
|
||||
if self.is_shutdown:
|
||||
raise ShutDown
|
||||
elif timeout < 0:
|
||||
raise ValueError("'timeout' must be a non-negative number")
|
||||
else:
|
||||
@@ -147,6 +171,8 @@ class Queue:
|
||||
if remaining <= 0.0:
|
||||
raise Full
|
||||
self.not_full.wait(remaining)
|
||||
if self.is_shutdown:
|
||||
raise ShutDown
|
||||
self._put(item)
|
||||
self.unfinished_tasks += 1
|
||||
self.not_empty.notify()
|
||||
@@ -161,14 +187,21 @@ class Queue:
|
||||
Otherwise ('block' is false), return an item if one is immediately
|
||||
available, else raise the Empty exception ('timeout' is ignored
|
||||
in that case).
|
||||
|
||||
Raises ShutDown if the queue has been shut down and is empty,
|
||||
or if the queue has been shut down immediately.
|
||||
'''
|
||||
with self.not_empty:
|
||||
if self.is_shutdown and not self._qsize():
|
||||
raise ShutDown
|
||||
if not block:
|
||||
if not self._qsize():
|
||||
raise Empty
|
||||
elif timeout is None:
|
||||
while not self._qsize():
|
||||
self.not_empty.wait()
|
||||
if self.is_shutdown and not self._qsize():
|
||||
raise ShutDown
|
||||
elif timeout < 0:
|
||||
raise ValueError("'timeout' must be a non-negative number")
|
||||
else:
|
||||
@@ -178,6 +211,8 @@ class Queue:
|
||||
if remaining <= 0.0:
|
||||
raise Empty
|
||||
self.not_empty.wait(remaining)
|
||||
if self.is_shutdown and not self._qsize():
|
||||
raise ShutDown
|
||||
item = self._get()
|
||||
self.not_full.notify()
|
||||
return item
|
||||
@@ -198,6 +233,29 @@ class Queue:
|
||||
'''
|
||||
return self.get(block=False)
|
||||
|
||||
def shutdown(self, immediate=False):
|
||||
'''Shut-down the queue, making queue gets and puts raise ShutDown.
|
||||
|
||||
By default, gets will only raise once the queue is empty. Set
|
||||
'immediate' to True to make gets raise immediately instead.
|
||||
|
||||
All blocked callers of put() and get() will be unblocked. If
|
||||
'immediate', a task is marked as done for each item remaining in
|
||||
the queue, which may unblock callers of join().
|
||||
'''
|
||||
with self.mutex:
|
||||
self.is_shutdown = True
|
||||
if immediate:
|
||||
while self._qsize():
|
||||
self._get()
|
||||
if self.unfinished_tasks > 0:
|
||||
self.unfinished_tasks -= 1
|
||||
# release all blocked threads in `join()`
|
||||
self.all_tasks_done.notify_all()
|
||||
# All getters need to re-check queue-empty to raise ShutDown
|
||||
self.not_empty.notify_all()
|
||||
self.not_full.notify_all()
|
||||
|
||||
# Override these methods to implement other queue organizations
|
||||
# (e.g. stack or priority queue).
|
||||
# These will only be called with appropriate locks held
|
||||
|
||||
Vendored
+416
@@ -2,6 +2,7 @@
|
||||
# to ensure the Queue locks remain stable.
|
||||
import itertools
|
||||
import random
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import unittest
|
||||
@@ -10,6 +11,8 @@ from test.support import gc_collect
|
||||
from test.support import import_helper
|
||||
from test.support import threading_helper
|
||||
|
||||
# queue module depends on threading primitives
|
||||
threading_helper.requires_working_threading(module=True)
|
||||
|
||||
py_queue = import_helper.import_fresh_module('queue', blocked=['_queue'])
|
||||
c_queue = import_helper.import_fresh_module('queue', fresh=['_queue'])
|
||||
@@ -239,6 +242,418 @@ class BaseQueueTestMixin(BlockingTestMixin):
|
||||
with self.assertRaises(self.queue.Full):
|
||||
q.put_nowait(4)
|
||||
|
||||
def test_shutdown_empty(self):
|
||||
q = self.type2test()
|
||||
q.shutdown()
|
||||
with self.assertRaises(self.queue.ShutDown):
|
||||
q.put("data")
|
||||
with self.assertRaises(self.queue.ShutDown):
|
||||
q.get()
|
||||
|
||||
def test_shutdown_nonempty(self):
|
||||
q = self.type2test()
|
||||
q.put("data")
|
||||
q.shutdown()
|
||||
q.get()
|
||||
with self.assertRaises(self.queue.ShutDown):
|
||||
q.get()
|
||||
|
||||
def test_shutdown_immediate(self):
|
||||
q = self.type2test()
|
||||
q.put("data")
|
||||
q.shutdown(immediate=True)
|
||||
with self.assertRaises(self.queue.ShutDown):
|
||||
q.get()
|
||||
|
||||
def test_shutdown_allowed_transitions(self):
|
||||
# allowed transitions would be from alive via shutdown to immediate
|
||||
q = self.type2test()
|
||||
self.assertFalse(q.is_shutdown)
|
||||
|
||||
q.shutdown()
|
||||
self.assertTrue(q.is_shutdown)
|
||||
|
||||
q.shutdown(immediate=True)
|
||||
self.assertTrue(q.is_shutdown)
|
||||
|
||||
q.shutdown(immediate=False)
|
||||
|
||||
def _shutdown_all_methods_in_one_thread(self, immediate):
|
||||
q = self.type2test(2)
|
||||
q.put("L")
|
||||
q.put_nowait("O")
|
||||
q.shutdown(immediate)
|
||||
|
||||
with self.assertRaises(self.queue.ShutDown):
|
||||
q.put("E")
|
||||
with self.assertRaises(self.queue.ShutDown):
|
||||
q.put_nowait("W")
|
||||
if immediate:
|
||||
with self.assertRaises(self.queue.ShutDown):
|
||||
q.get()
|
||||
with self.assertRaises(self.queue.ShutDown):
|
||||
q.get_nowait()
|
||||
with self.assertRaises(ValueError):
|
||||
q.task_done()
|
||||
q.join()
|
||||
else:
|
||||
self.assertIn(q.get(), "LO")
|
||||
q.task_done()
|
||||
self.assertIn(q.get(), "LO")
|
||||
q.task_done()
|
||||
q.join()
|
||||
# on shutdown(immediate=False)
|
||||
# when queue is empty, should raise ShutDown Exception
|
||||
with self.assertRaises(self.queue.ShutDown):
|
||||
q.get() # p.get(True)
|
||||
with self.assertRaises(self.queue.ShutDown):
|
||||
q.get_nowait() # p.get(False)
|
||||
with self.assertRaises(self.queue.ShutDown):
|
||||
q.get(True, 1.0)
|
||||
|
||||
def test_shutdown_all_methods_in_one_thread(self):
|
||||
return self._shutdown_all_methods_in_one_thread(False)
|
||||
|
||||
def test_shutdown_immediate_all_methods_in_one_thread(self):
|
||||
return self._shutdown_all_methods_in_one_thread(True)
|
||||
|
||||
def _write_msg_thread(self, q, n, results,
|
||||
i_when_exec_shutdown, event_shutdown,
|
||||
barrier_start):
|
||||
# All `write_msg_threads`
|
||||
# put several items into the queue.
|
||||
for i in range(0, i_when_exec_shutdown//2):
|
||||
q.put((i, 'LOYD'))
|
||||
# Wait for the barrier to be complete.
|
||||
barrier_start.wait()
|
||||
|
||||
for i in range(i_when_exec_shutdown//2, n):
|
||||
try:
|
||||
q.put((i, "YDLO"))
|
||||
except self.queue.ShutDown:
|
||||
results.append(False)
|
||||
break
|
||||
|
||||
# Trigger queue shutdown.
|
||||
if i == i_when_exec_shutdown:
|
||||
# Only one thread should call shutdown().
|
||||
if not event_shutdown.is_set():
|
||||
event_shutdown.set()
|
||||
results.append(True)
|
||||
|
||||
def _read_msg_thread(self, q, results, barrier_start):
|
||||
# Get at least one item.
|
||||
q.get(True)
|
||||
q.task_done()
|
||||
# Wait for the barrier to be complete.
|
||||
barrier_start.wait()
|
||||
while True:
|
||||
try:
|
||||
q.get(False)
|
||||
q.task_done()
|
||||
except self.queue.ShutDown:
|
||||
results.append(True)
|
||||
break
|
||||
except self.queue.Empty:
|
||||
pass
|
||||
|
||||
def _shutdown_thread(self, q, results, event_end, immediate):
|
||||
event_end.wait()
|
||||
q.shutdown(immediate)
|
||||
results.append(q.qsize() == 0)
|
||||
|
||||
def _join_thread(self, q, barrier_start):
|
||||
# Wait for the barrier to be complete.
|
||||
barrier_start.wait()
|
||||
q.join()
|
||||
|
||||
def _shutdown_all_methods_in_many_threads(self, immediate):
|
||||
# Run a 'multi-producers/consumers queue' use case,
|
||||
# with enough items into the queue.
|
||||
# When shutdown, all running threads will be joined.
|
||||
q = self.type2test()
|
||||
ps = []
|
||||
res_puts = []
|
||||
res_gets = []
|
||||
res_shutdown = []
|
||||
write_threads = 4
|
||||
read_threads = 6
|
||||
join_threads = 2
|
||||
nb_msgs = 1024*64
|
||||
nb_msgs_w = nb_msgs // write_threads
|
||||
when_exec_shutdown = nb_msgs_w // 2
|
||||
# Use of a Barrier to ensure that
|
||||
# - all write threads put all their items into the queue,
|
||||
# - all read thread get at least one item from the queue,
|
||||
# and keep on running until shutdown.
|
||||
# The join thread is started only when shutdown is immediate.
|
||||
nparties = write_threads + read_threads
|
||||
if immediate:
|
||||
nparties += join_threads
|
||||
barrier_start = threading.Barrier(nparties)
|
||||
ev_exec_shutdown = threading.Event()
|
||||
lprocs = [
|
||||
(self._write_msg_thread, write_threads, (q, nb_msgs_w, res_puts,
|
||||
when_exec_shutdown, ev_exec_shutdown,
|
||||
barrier_start)),
|
||||
(self._read_msg_thread, read_threads, (q, res_gets, barrier_start)),
|
||||
(self._shutdown_thread, 1, (q, res_shutdown, ev_exec_shutdown, immediate)),
|
||||
]
|
||||
if immediate:
|
||||
lprocs.append((self._join_thread, join_threads, (q, barrier_start)))
|
||||
# start all threads.
|
||||
for func, n, args in lprocs:
|
||||
for i in range(n):
|
||||
ps.append(threading.Thread(target=func, args=args))
|
||||
ps[-1].start()
|
||||
for thread in ps:
|
||||
thread.join()
|
||||
|
||||
self.assertTrue(True in res_puts)
|
||||
self.assertEqual(res_gets.count(True), read_threads)
|
||||
if immediate:
|
||||
self.assertListEqual(res_shutdown, [True])
|
||||
self.assertTrue(q.empty())
|
||||
|
||||
def test_shutdown_all_methods_in_many_threads(self):
|
||||
return self._shutdown_all_methods_in_many_threads(False)
|
||||
|
||||
def test_shutdown_immediate_all_methods_in_many_threads(self):
|
||||
return self._shutdown_all_methods_in_many_threads(True)
|
||||
|
||||
def _get(self, q, go, results, shutdown=False):
|
||||
go.wait()
|
||||
try:
|
||||
msg = q.get()
|
||||
results.append(not shutdown)
|
||||
return not shutdown
|
||||
except self.queue.ShutDown:
|
||||
results.append(shutdown)
|
||||
return shutdown
|
||||
|
||||
def _get_shutdown(self, q, go, results):
|
||||
return self._get(q, go, results, True)
|
||||
|
||||
def _get_task_done(self, q, go, results):
|
||||
go.wait()
|
||||
try:
|
||||
msg = q.get()
|
||||
q.task_done()
|
||||
results.append(True)
|
||||
return msg
|
||||
except self.queue.ShutDown:
|
||||
results.append(False)
|
||||
return False
|
||||
|
||||
def _put(self, q, msg, go, results, shutdown=False):
|
||||
go.wait()
|
||||
try:
|
||||
q.put(msg)
|
||||
results.append(not shutdown)
|
||||
return not shutdown
|
||||
except self.queue.ShutDown:
|
||||
results.append(shutdown)
|
||||
return shutdown
|
||||
|
||||
def _put_shutdown(self, q, msg, go, results):
|
||||
return self._put(q, msg, go, results, True)
|
||||
|
||||
def _join(self, q, results, shutdown=False):
|
||||
try:
|
||||
q.join()
|
||||
results.append(not shutdown)
|
||||
return not shutdown
|
||||
except self.queue.ShutDown:
|
||||
results.append(shutdown)
|
||||
return shutdown
|
||||
|
||||
def _join_shutdown(self, q, results):
|
||||
return self._join(q, results, True)
|
||||
|
||||
def _shutdown_get(self, immediate):
|
||||
q = self.type2test(2)
|
||||
results = []
|
||||
go = threading.Event()
|
||||
q.put("Y")
|
||||
q.put("D")
|
||||
# queue full
|
||||
|
||||
if immediate:
|
||||
thrds = (
|
||||
(self._get_shutdown, (q, go, results)),
|
||||
(self._get_shutdown, (q, go, results)),
|
||||
)
|
||||
else:
|
||||
thrds = (
|
||||
# on shutdown(immediate=False)
|
||||
# one of these threads should raise Shutdown
|
||||
(self._get, (q, go, results)),
|
||||
(self._get, (q, go, results)),
|
||||
(self._get, (q, go, results)),
|
||||
)
|
||||
threads = []
|
||||
for func, params in thrds:
|
||||
threads.append(threading.Thread(target=func, args=params))
|
||||
threads[-1].start()
|
||||
q.shutdown(immediate)
|
||||
go.set()
|
||||
for t in threads:
|
||||
t.join()
|
||||
if immediate:
|
||||
self.assertListEqual(results, [True, True])
|
||||
else:
|
||||
self.assertListEqual(sorted(results), [False] + [True]*(len(thrds)-1))
|
||||
|
||||
def test_shutdown_get(self):
|
||||
return self._shutdown_get(False)
|
||||
|
||||
def test_shutdown_immediate_get(self):
|
||||
return self._shutdown_get(True)
|
||||
|
||||
def _shutdown_put(self, immediate):
|
||||
q = self.type2test(2)
|
||||
results = []
|
||||
go = threading.Event()
|
||||
q.put("Y")
|
||||
q.put("D")
|
||||
# queue fulled
|
||||
|
||||
thrds = (
|
||||
(self._put_shutdown, (q, "E", go, results)),
|
||||
(self._put_shutdown, (q, "W", go, results)),
|
||||
)
|
||||
threads = []
|
||||
for func, params in thrds:
|
||||
threads.append(threading.Thread(target=func, args=params))
|
||||
threads[-1].start()
|
||||
q.shutdown()
|
||||
go.set()
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
self.assertEqual(results, [True]*len(thrds))
|
||||
|
||||
def test_shutdown_put(self):
|
||||
return self._shutdown_put(False)
|
||||
|
||||
def test_shutdown_immediate_put(self):
|
||||
return self._shutdown_put(True)
|
||||
|
||||
def _shutdown_join(self, immediate):
|
||||
q = self.type2test()
|
||||
results = []
|
||||
q.put("Y")
|
||||
go = threading.Event()
|
||||
nb = q.qsize()
|
||||
|
||||
thrds = (
|
||||
(self._join, (q, results)),
|
||||
(self._join, (q, results)),
|
||||
)
|
||||
threads = []
|
||||
for func, params in thrds:
|
||||
threads.append(threading.Thread(target=func, args=params))
|
||||
threads[-1].start()
|
||||
if not immediate:
|
||||
res = []
|
||||
for i in range(nb):
|
||||
threads.append(threading.Thread(target=self._get_task_done, args=(q, go, res)))
|
||||
threads[-1].start()
|
||||
q.shutdown(immediate)
|
||||
go.set()
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
self.assertEqual(results, [True]*len(thrds))
|
||||
|
||||
def test_shutdown_immediate_join(self):
|
||||
return self._shutdown_join(True)
|
||||
|
||||
def test_shutdown_join(self):
|
||||
return self._shutdown_join(False)
|
||||
|
||||
def _shutdown_put_join(self, immediate):
|
||||
q = self.type2test(2)
|
||||
results = []
|
||||
go = threading.Event()
|
||||
q.put("Y")
|
||||
# queue not fulled
|
||||
|
||||
thrds = (
|
||||
(self._put_shutdown, (q, "E", go, results)),
|
||||
(self._join, (q, results)),
|
||||
)
|
||||
threads = []
|
||||
for func, params in thrds:
|
||||
threads.append(threading.Thread(target=func, args=params))
|
||||
threads[-1].start()
|
||||
self.assertEqual(q.unfinished_tasks, 1)
|
||||
|
||||
q.shutdown(immediate)
|
||||
go.set()
|
||||
|
||||
if immediate:
|
||||
with self.assertRaises(self.queue.ShutDown):
|
||||
q.get_nowait()
|
||||
else:
|
||||
result = q.get()
|
||||
self.assertEqual(result, "Y")
|
||||
q.task_done()
|
||||
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
self.assertEqual(results, [True]*len(thrds))
|
||||
|
||||
def test_shutdown_immediate_put_join(self):
|
||||
return self._shutdown_put_join(True)
|
||||
|
||||
def test_shutdown_put_join(self):
|
||||
return self._shutdown_put_join(False)
|
||||
|
||||
def test_shutdown_get_task_done_join(self):
|
||||
q = self.type2test(2)
|
||||
results = []
|
||||
go = threading.Event()
|
||||
q.put("Y")
|
||||
q.put("D")
|
||||
self.assertEqual(q.unfinished_tasks, q.qsize())
|
||||
|
||||
thrds = (
|
||||
(self._get_task_done, (q, go, results)),
|
||||
(self._get_task_done, (q, go, results)),
|
||||
(self._join, (q, results)),
|
||||
(self._join, (q, results)),
|
||||
)
|
||||
threads = []
|
||||
for func, params in thrds:
|
||||
threads.append(threading.Thread(target=func, args=params))
|
||||
threads[-1].start()
|
||||
go.set()
|
||||
q.shutdown(False)
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
self.assertEqual(results, [True]*len(thrds))
|
||||
|
||||
def test_shutdown_pending_get(self):
|
||||
def get():
|
||||
try:
|
||||
results.append(q.get())
|
||||
except Exception as e:
|
||||
results.append(e)
|
||||
|
||||
q = self.type2test()
|
||||
results = []
|
||||
get_thread = threading.Thread(target=get)
|
||||
get_thread.start()
|
||||
q.shutdown(immediate=False)
|
||||
get_thread.join(timeout=10.0)
|
||||
self.assertFalse(get_thread.is_alive())
|
||||
self.assertEqual(len(results), 1)
|
||||
self.assertIsInstance(results[0], self.queue.ShutDown)
|
||||
|
||||
|
||||
class QueueTest(BaseQueueTestMixin):
|
||||
|
||||
def setUp(self):
|
||||
@@ -289,6 +704,7 @@ class CPriorityQueueTest(PriorityQueueTest, unittest.TestCase):
|
||||
# A Queue subclass that can provoke failure at a moment's notice :)
|
||||
class FailingQueueException(Exception): pass
|
||||
|
||||
|
||||
class FailingQueueTest(BlockingTestMixin):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
Reference in new issue
Block a user