1
'''A multi-producer, multi-consumer queue.'''
5
from collections import deque
6
from heapq import heappush, heappop
7
from time import monotonic as time
9
from _queue import SimpleQueue
25
from _queue import Empty
27
class Empty(Exception):
28
'Exception raised by Queue.get(block=0)/get_nowait().'
31
class Full(Exception):
32
'Exception raised by Queue.put(block=0)/put_nowait().'
36
class ShutDown(Exception):
37
'''Raised when put/get with shut-down queue.'''
41
'''Create a queue object with a given maximum size.
43
If maxsize is <= 0, the queue size is infinite.
46
def __init__(self, maxsize=0):
47
self.maxsize = maxsize
50
# mutex must be held whenever the queue is mutating. All methods
51
# that acquire mutex must release it before returning. mutex
52
# is shared between the three conditions, so acquiring and
53
# releasing the conditions also acquires and releases mutex.
54
self.mutex = threading.Lock()
56
# Notify not_empty whenever an item is added to the queue; a
57
# thread waiting to get is notified then.
58
self.not_empty = threading.Condition(self.mutex)
60
# Notify not_full whenever an item is removed from the queue;
61
# a thread waiting to put is notified then.
62
self.not_full = threading.Condition(self.mutex)
64
# Notify all_tasks_done whenever the number of unfinished tasks
65
# drops to zero; thread waiting to join() is notified to resume
66
self.all_tasks_done = threading.Condition(self.mutex)
67
self.unfinished_tasks = 0
69
# Queue shutdown state
70
self.is_shutdown = False
73
'''Indicate that a formerly enqueued task is complete.
75
Used by Queue consumer threads. For each get() used to fetch a task,
76
a subsequent call to task_done() tells the queue that the processing
77
on the task is complete.
79
If a join() is currently blocking, it will resume when all items
80
have been processed (meaning that a task_done() call was received
81
for every item that had been put() into the queue).
83
Raises a ValueError if called more times than there were items
86
with self.all_tasks_done:
87
unfinished = self.unfinished_tasks - 1
90
raise ValueError('task_done() called too many times')
91
self.all_tasks_done.notify_all()
92
self.unfinished_tasks = unfinished
95
'''Blocks until all items in the Queue have been gotten and processed.
97
The count of unfinished tasks goes up whenever an item is added to the
98
queue. The count goes down whenever a consumer thread calls task_done()
99
to indicate the item was retrieved and all work on it is complete.
101
When the count of unfinished tasks drops to zero, join() unblocks.
103
with self.all_tasks_done:
104
while self.unfinished_tasks:
105
self.all_tasks_done.wait()
108
'''Return the approximate size of the queue (not reliable!).'''
113
'''Return True if the queue is empty, False otherwise (not reliable!).
115
This method is likely to be removed at some point. Use qsize() == 0
116
as a direct substitute, but be aware that either approach risks a race
117
condition where a queue can grow before the result of empty() or
120
To create code that needs to wait for all queued tasks to be
121
completed, the preferred technique is to use the join() method.
124
return not self._qsize()
127
'''Return True if the queue is full, False otherwise (not reliable!).
129
This method is likely to be removed at some point. Use qsize() >= n
130
as a direct substitute, but be aware that either approach risks a race
131
condition where a queue can shrink before the result of full() or
135
return 0 < self.maxsize <= self._qsize()
137
def put(self, item, block=True, timeout=None):
138
'''Put an item into the queue.
140
If optional args 'block' is true and 'timeout' is None (the default),
141
block if necessary until a free slot is available. If 'timeout' is
142
a non-negative number, it blocks at most 'timeout' seconds and raises
143
the Full exception if no free slot was available within that time.
144
Otherwise ('block' is false), put an item on the queue if a free slot
145
is immediately available, else raise the Full exception ('timeout'
146
is ignored in that case).
148
Raises ShutDown if the queue has been shut down.
155
if self._qsize() >= self.maxsize:
157
elif timeout is None:
158
while self._qsize() >= self.maxsize:
163
raise ValueError("'timeout' must be a non-negative number")
165
endtime = time() + timeout
166
while self._qsize() >= self.maxsize:
167
remaining = endtime - time()
170
self.not_full.wait(remaining)
174
self.unfinished_tasks += 1
175
self.not_empty.notify()
177
def get(self, block=True, timeout=None):
178
'''Remove and return an item from the queue.
180
If optional args 'block' is true and 'timeout' is None (the default),
181
block if necessary until an item is available. If 'timeout' is
182
a non-negative number, it blocks at most 'timeout' seconds and raises
183
the Empty exception if no item was available within that time.
184
Otherwise ('block' is false), return an item if one is immediately
185
available, else raise the Empty exception ('timeout' is ignored
188
Raises ShutDown if the queue has been shut down and is empty,
189
or if the queue has been shut down immediately.
191
1
with self.not_empty:
192
if self.is_shutdown and not self._qsize():
195
if not self._qsize():
197
elif timeout is None:
198
while not self._qsize():
199
5
18
self.not_empty.wait()
200
if self.is_shutdown and not self._qsize():
203
raise ValueError("'timeout' must be a non-negative number")
205
endtime = time() + timeout
206
while not self._qsize():
207
remaining = endtime - time()
210
self.not_empty.wait(remaining)
211
if self.is_shutdown and not self._qsize():
214
self.not_full.notify()
217
def put_nowait(self, item):
218
'''Put an item into the queue without blocking.
220
Only enqueue the item if a free slot is immediately available.
221
Otherwise raise the Full exception.
223
return self.put(item, block=False)
225
def get_nowait(self):
226
'''Remove and return an item from the queue without blocking.
228
Only get an item if one is immediately available. Otherwise
229
raise the Empty exception.
231
return self.get(block=False)
233
def shutdown(self, immediate=False):
234
'''Shut-down the queue, making queue gets and puts raise ShutDown.
236
By default, gets will only raise once the queue is empty. Set
237
'immediate' to True to make gets raise immediately instead.
239
All blocked callers of put() and get() will be unblocked.
241
If 'immediate', the queue is drained and unfinished tasks
242
is reduced by the number of drained tasks. If unfinished tasks
243
is reduced to zero, callers of Queue.join are unblocked.
246
self.is_shutdown = True
250
if self.unfinished_tasks > 0:
251
self.unfinished_tasks -= 1
252
# release all blocked threads in `join()`
253
self.all_tasks_done.notify_all()
254
# All getters need to re-check queue-empty to raise ShutDown
255
self.not_empty.notify_all()
256
self.not_full.notify_all()
258
# Override these methods to implement other queue organizations
259
# (e.g. stack or priority queue).
260
# These will only be called with appropriate locks held
262
# Initialize the queue representation
263
def _init(self, maxsize):
267
return len(self.queue)
269
# Put a new item in the queue
270
def _put(self, item):
271
self.queue.append(item)
273
# Get an item from the queue
275
return self.queue.popleft()
277
__class_getitem__ = classmethod(types.GenericAlias)
280
class PriorityQueue(Queue):
281
'''Variant of Queue that retrieves open entries in priority order (lowest first).
283
Entries are typically tuples of the form: (priority number, data).
286
def _init(self, maxsize):
290
return len(self.queue)
292
def _put(self, item):
293
heappush(self.queue, item)
296
return heappop(self.queue)
299
class LifoQueue(Queue):
300
'''Variant of Queue that retrieves most recently added entries first.'''
302
def _init(self, maxsize):
306
return len(self.queue)
308
def _put(self, item):
309
self.queue.append(item)
312
return self.queue.pop()
315
class _PySimpleQueue:
316
'''Simple, unbounded FIFO queue.
318
This pure Python implementation is not reentrant.
320
# Note: while this pure Python version provides fairness
321
# (by using a threading.Semaphore which is itself fair, being based
322
# on threading.Condition), fairness is not part of the API contract.
323
# This allows the C version to use a different implementation.
326
self._queue = deque()
327
self._count = threading.Semaphore(0)
329
def put(self, item, block=True, timeout=None):
330
'''Put the item on the queue.
332
The optional 'block' and 'timeout' arguments are ignored, as this method
333
never blocks. They are provided for compatibility with the Queue class.
335
self._queue.append(item)
336
self._count.release()
338
def get(self, block=True, timeout=None):
339
'''Remove and return an item from the queue.
341
If optional args 'block' is true and 'timeout' is None (the default),
342
block if necessary until an item is available. If 'timeout' is
343
a non-negative number, it blocks at most 'timeout' seconds and raises
344
the Empty exception if no item was available within that time.
345
Otherwise ('block' is false), return an item if one is immediately
346
available, else raise the Empty exception ('timeout' is ignored
349
if timeout is not None and timeout < 0:
350
raise ValueError("'timeout' must be a non-negative number")
351
if not self._count.acquire(block, timeout):
353
return self._queue.popleft()
355
def put_nowait(self, item):
356
'''Put an item into the queue without blocking.
358
This is exactly equivalent to `put(item, block=False)` and is only provided
359
for compatibility with the Queue class.
361
return self.put(item, block=False)
363
def get_nowait(self):
364
'''Remove and return an item from the queue without blocking.
366
Only get an item if one is immediately available. Otherwise
367
raise the Empty exception.
369
return self.get(block=False)
372
'''Return True if the queue is empty, False otherwise (not reliable!).'''
373
return len(self._queue) == 0
376
'''Return the approximate size of the queue (not reliable!).'''
377
return len(self._queue)
379
__class_getitem__ = classmethod(types.GenericAlias)
382
if SimpleQueue is None:
383
SimpleQueue = _PySimpleQueue