11"""A multi-producer, multi-consumer queue."""
22
3- from time import time as _time , sleep as _sleep
3+ from time import time as _time
44from collections import deque
55
66__all__ = ['Empty' , 'Full' , 'Queue' ]
@@ -20,14 +20,21 @@ def __init__(self, maxsize=0):
2020 If maxsize is <= 0, the queue size is infinite.
2121 """
2222 try :
23- import thread
23+ import threading
2424 except ImportError :
25- import dummy_thread as thread
25+ import dummy_threading as threading
2626 self ._init (maxsize )
27- self .mutex = thread .allocate_lock ()
28- self .esema = thread .allocate_lock ()
29- self .esema .acquire ()
30- self .fsema = thread .allocate_lock ()
27+ # mutex must be held whenever the queue is mutating. All methods
28+ # that acquire mutex must release it before returning. mutex
29+ # is shared between the two conditions, so acquiring and
30+ # releasing the conditions also acquires and releases mutex.
31+ self .mutex = threading .Lock ()
32+ # Notify not_empty whenever an item is added to the queue; a
33+ # thread waiting to get is notified then.
34+ self .not_empty = threading .Condition (self .mutex )
35+ # Notify not_full whenever an item is removed from the queue;
36+ # a thread waiting to put is notified then.
37+ self .not_full = threading .Condition (self .mutex )
3138
3239 def qsize (self ):
3340 """Return the approximate size of the queue (not reliable!)."""
@@ -61,59 +68,42 @@ def put(self, item, block=True, timeout=None):
6168 is immediately available, else raise the Full exception ('timeout'
6269 is ignored in that case).
6370 """
64- if block :
71+ if not block :
72+ return self .put_nowait (item )
73+ self .not_full .acquire ()
74+ try :
6575 if timeout is None :
66- # blocking, w/o timeout, i.e. forever
67- self .fsema .acquire ()
68- elif timeout >= 0 :
69- # waiting max. 'timeout' seconds.
70- # this code snipped is from threading.py: _Event.wait():
71- # Balancing act: We can't afford a pure busy loop, so we
72- # have to sleep; but if we sleep the whole timeout time,
73- # we'll be unresponsive. The scheme here sleeps very
74- # little at first, longer as time goes on, but never longer
75- # than 20 times per second (or the timeout time remaining).
76- delay = 0.0005 # 500 us -> initial delay of 1 ms
76+ while self ._full ():
77+ self .not_full .wait ()
78+ else :
79+ if timeout < 0 :
80+ raise ValueError ("'timeout' must be a positive number" )
7781 endtime = _time () + timeout
78- while True :
79- if self .fsema .acquire (0 ):
80- break
82+ while self ._full ():
8183 remaining = endtime - _time ()
82- if remaining <= 0 : #time is over and no slot was free
84+ if remaining < 0.0 :
8385 raise Full
84- delay = min (delay * 2 , remaining , .05 )
85- _sleep (delay ) #reduce CPU usage by using a sleep
86- else :
87- raise ValueError ("'timeout' must be a positive number" )
88- elif not self .fsema .acquire (0 ):
89- raise Full
90- self .mutex .acquire ()
91- release_fsema = True
92- try :
93- was_empty = self ._empty ()
86+ self .not_full .wait (remaining )
9487 self ._put (item )
95- # If we fail before here, the empty state has
96- # not changed, so we can skip the release of esema
97- if was_empty :
98- self .esema .release ()
99- # If we fail before here, the queue can not be full, so
100- # release_full_sema remains True
101- release_fsema = not self ._full ()
88+ self .not_empty .notify ()
10289 finally :
103- # Catching system level exceptions here (RecursionDepth,
104- # OutOfMemory, etc) - so do as little as possible in terms
105- # of Python calls.
106- if release_fsema :
107- self .fsema .release ()
108- self .mutex .release ()
90+ self .not_full .release ()
10991
11092 def put_nowait (self , item ):
11193 """Put an item into the queue without blocking.
11294
11395 Only enqueue the item if a free slot is immediately available.
11496 Otherwise raise the Full exception.
11597 """
116- return self .put (item , False )
98+ self .not_full .acquire ()
99+ try :
100+ if self ._full ():
101+ raise Full
102+ else :
103+ self ._put (item )
104+ self .not_empty .notify ()
105+ finally :
106+ self .not_full .release ()
117107
118108 def get (self , block = True , timeout = None ):
119109 """Remove and return an item from the queue.
@@ -126,57 +116,44 @@ def get(self, block=True, timeout=None):
126116 available, else raise the Empty exception ('timeout' is ignored
127117 in that case).
128118 """
129- if block :
119+ if not block :
120+ return self .get_nowait ()
121+ self .not_empty .acquire ()
122+ try :
130123 if timeout is None :
131- # blocking, w/o timeout, i.e. forever
132- self .esema .acquire ()
133- elif timeout >= 0 :
134- # waiting max. 'timeout' seconds.
135- # this code snipped is from threading.py: _Event.wait():
136- # Balancing act: We can't afford a pure busy loop, so we
137- # have to sleep; but if we sleep the whole timeout time,
138- # we'll be unresponsive. The scheme here sleeps very
139- # little at first, longer as time goes on, but never longer
140- # than 20 times per second (or the timeout time remaining).
141- delay = 0.0005 # 500 us -> initial delay of 1 ms
124+ while self ._empty ():
125+ self .not_empty .wait ()
126+ else :
127+ if timeout < 0 :
128+ raise ValueError ("'timeout' must be a positive number" )
142129 endtime = _time () + timeout
143- while 1 :
144- if self .esema .acquire (0 ):
145- break
130+ while self ._empty ():
146131 remaining = endtime - _time ()
147- if remaining <= 0 : #time is over and no element arrived
132+ if remaining < 0.0 :
148133 raise Empty
149- delay = min (delay * 2 , remaining , .05 )
150- _sleep (delay ) #reduce CPU usage by using a sleep
151- else :
152- raise ValueError ("'timeout' must be a positive number" )
153- elif not self .esema .acquire (0 ):
154- raise Empty
155- self .mutex .acquire ()
156- release_esema = True
157- try :
158- was_full = self ._full ()
134+ self .not_empty .wait (remaining )
159135 item = self ._get ()
160- # If we fail before here, the full state has
161- # not changed, so we can skip the release of fsema
162- if was_full :
163- self .fsema .release ()
164- # Failure means empty state also unchanged - release_esema
165- # remains True.
166- release_esema = not self ._empty ()
136+ self .not_full .notify ()
137+ return item
167138 finally :
168- if release_esema :
169- self .esema .release ()
170- self .mutex .release ()
171- return item
139+ self .not_empty .release ()
172140
173141 def get_nowait (self ):
174142 """Remove and return an item from the queue without blocking.
175143
176144 Only get an item if one is immediately available. Otherwise
177145 raise the Empty exception.
178146 """
179- return self .get (False )
147+ self .not_empty .acquire ()
148+ try :
149+ if self ._empty ():
150+ raise Empty
151+ else :
152+ item = self ._get ()
153+ self .not_full .notify ()
154+ return item
155+ finally :
156+ self .not_empty .release ()
180157
181158 # Override these methods to implement other queue organizations
182159 # (e.g. stack or priority queue).
0 commit comments