Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 77ac429

Browse files
committed
Patch #572628: Optional timeouts for put and get.
1 parent d98d25e commit 77ac429

4 files changed

Lines changed: 137 additions & 36 deletions

File tree

Doc/lib/libqueue.tex

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,35 +54,47 @@ \subsection{Queue Objects}
5454
\end{methoddesc}
5555

5656
\begin{methoddesc}{empty}{}
57-
Return \code{1} if the queue is empty, \code{0} otherwise. Because
58-
of multithreading semantics, this is not reliable.
57+
Return \code{True} if the queue is empty, \code{False} otherwise.
58+
Becauseof multithreading semantics, this is not reliable.
5959
\end{methoddesc}
6060

6161
\begin{methoddesc}{full}{}
62-
Return \code{1} if the queue is full, \code{0} otherwise. Because of
63-
multithreading semantics, this is not reliable.
62+
Return \code{True} if the queue is full, \code{False} otherwise.
63+
Because of multithreading semantics, this is not reliable.
6464
\end{methoddesc}
6565

66-
\begin{methoddesc}{put}{item\optional{, block}}
67-
Put \var{item} into the queue. If optional argument \var{block} is 1
68-
(the default), block if necessary until a free slot is available.
69-
Otherwise (\var{block} is 0), put \var{item} on the queue if a free
66+
\begin{methoddesc}{put}{item\optional{, block\optional{, timeout}}}
67+
Put \var{item} into the queue. If optional args \var{block} is true
68+
and \var{timeout} is None (the default), block if necessary until a
69+
free slot is available. If \var{timeout} is a positive number, it
70+
blocks at most \var{timeout} seconds and raises the \exception{Full}
71+
exception if no free slot was available within that time.
72+
Otherwise (\var{block} is false), put an item on the queue if a free
7073
slot is immediately available, else raise the \exception{Full}
71-
exception.
74+
exception (\var{timeout} is ignored in that case).
75+
76+
\versionadded[the timeout parameter]{2.3}
77+
7278
\end{methoddesc}
7379

7480
\begin{methoddesc}{put_nowait}{item}
75-
Equivalent to \code{put(\var{item}, 0)}.
81+
Equivalent to \code{put(\var{item}, False)}.
7682
\end{methoddesc}
7783

78-
\begin{methoddesc}{get}{\optional{block}}
79-
Remove and return an item from the queue. If optional argument
80-
\var{block} is 1 (the default), block if necessary until an item is
81-
available. Otherwise (\var{block} is 0), return an item if one is
82-
immediately available, else raise the
83-
\exception{Empty} exception.
84+
\begin{methoddesc}{get}{\optional{block\optional{, timeout}}}
85+
Remove and return an item from the queue. If optional args
86+
\var{block} is true and \var{timeout} is None (the default),
87+
block if necessary until an item is available. If \var{timeout} is
88+
a positive number, it blocks at most \var{timeout} seconds and raises
89+
the \exception{Empty} exception if no item was available within that
90+
time. Otherwise (\var{block} is false), return an item if one is
91+
immediately available, else raise the \exception{Empty} exception
92+
(\var{timeout} is ignored in that case).
93+
94+
\versionadded[the timeout parameter]{2.3}
95+
8496
\end{methoddesc}
8597

8698
\begin{methoddesc}{get_nowait}{}
87-
Equivalent to \code{get(0)}.
99+
Equivalent to \code{get(False)}.
88100
\end{methoddesc}

Lib/Queue.py

Lines changed: 69 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""A multi-producer, multi-consumer queue."""
22

3+
from time import time as _time, sleep as _sleep
4+
35
class Empty(Exception):
46
"Exception raised by Queue.get(block=0)/get_nowait()."
57
pass
@@ -29,29 +31,54 @@ def qsize(self):
2931
return n
3032

3133
def empty(self):
32-
"""Return 1 if the queue is empty, 0 otherwise (not reliable!)."""
34+
"""Return True if the queue is empty, False otherwise (not reliable!)."""
3335
self.mutex.acquire()
3436
n = self._empty()
3537
self.mutex.release()
3638
return n
3739

3840
def full(self):
39-
"""Return 1 if the queue is full, 0 otherwise (not reliable!)."""
41+
"""Return True if the queue is full, False otherwise (not reliable!)."""
4042
self.mutex.acquire()
4143
n = self._full()
4244
self.mutex.release()
4345
return n
4446

45-
def put(self, item, block=1):
47+
def put(self, item, block=True, timeout=None):
4648
"""Put an item into the queue.
4749
48-
If optional arg 'block' is 1 (the default), block if
49-
necessary until a free slot is available. Otherwise (block
50-
is 0), put an item on the queue if a free slot is immediately
51-
available, else raise the Full exception.
50+
If optional args 'block' is true and 'timeout' is None (the default),
51+
block if necessary until a free slot is available. If 'timeout' is
52+
a positive number, it blocks at most 'timeout' seconds and raises
53+
the Full exception if no free slot was available within that time.
54+
Otherwise ('block' is false), put an item on the queue if a free slot
55+
is immediately available, else raise the Full exception ('timeout'
56+
is ignored in that case).
5257
"""
5358
if block:
54-
self.fsema.acquire()
59+
if timeout is None:
60+
# blocking, w/o timeout, i.e. forever
61+
self.fsema.acquire()
62+
elif timeout >= 0:
63+
# waiting max. 'timeout' seconds.
64+
# this code snipped is from threading.py: _Event.wait():
65+
# Balancing act: We can't afford a pure busy loop, so we
66+
# have to sleep; but if we sleep the whole timeout time,
67+
# we'll be unresponsive. The scheme here sleeps very
68+
# little at first, longer as time goes on, but never longer
69+
# than 20 times per second (or the timeout time remaining).
70+
delay = 0.0005 # 500 us -> initial delay of 1 ms
71+
endtime = _time() + timeout
72+
while True:
73+
if self.fsema.acquire(0):
74+
break
75+
remaining = endtime - _time()
76+
if remaining <= 0: #time is over and no slot was free
77+
raise Full
78+
delay = min(delay * 2, remaining, .05)
79+
_sleep(delay) #reduce CPU usage by using a sleep
80+
else:
81+
raise ValueError("'timeout' must be a positive number")
5582
elif not self.fsema.acquire(0):
5683
raise Full
5784
self.mutex.acquire()
@@ -80,18 +107,43 @@ def put_nowait(self, item):
80107
Only enqueue the item if a free slot is immediately available.
81108
Otherwise raise the Full exception.
82109
"""
83-
return self.put(item, 0)
110+
return self.put(item, False)
84111

85-
def get(self, block=1):
112+
def get(self, block=True, timeout=None):
86113
"""Remove and return an item from the queue.
87114
88-
If optional arg 'block' is 1 (the default), block if
89-
necessary until an item is available. Otherwise (block is 0),
90-
return an item if one is immediately available, else raise the
91-
Empty exception.
115+
If optional args 'block' is true and 'timeout' is None (the default),
116+
block if necessary until an item is available. If 'timeout' is
117+
a positive number, it blocks at most 'timeout' seconds and raises
118+
the Empty exception if no item was available within that time.
119+
Otherwise ('block' is false), return an item if one is immediately
120+
available, else raise the Empty exception ('timeout' is ignored
121+
in that case).
92122
"""
93123
if block:
94-
self.esema.acquire()
124+
if timeout is None:
125+
# blocking, w/o timeout, i.e. forever
126+
self.esema.acquire()
127+
elif timeout >= 0:
128+
# waiting max. 'timeout' seconds.
129+
# this code snipped is from threading.py: _Event.wait():
130+
# Balancing act: We can't afford a pure busy loop, so we
131+
# have to sleep; but if we sleep the whole timeout time,
132+
# we'll be unresponsive. The scheme here sleeps very
133+
# little at first, longer as time goes on, but never longer
134+
# than 20 times per second (or the timeout time remaining).
135+
delay = 0.0005 # 500 us -> initial delay of 1 ms
136+
endtime = _time() + timeout
137+
while 1:
138+
if self.esema.acquire(0):
139+
break
140+
remaining = endtime - _time()
141+
if remaining <= 0: #time is over and no element arrived
142+
raise Empty
143+
delay = min(delay * 2, remaining, .05)
144+
_sleep(delay) #reduce CPU usage by using a sleep
145+
else:
146+
raise ValueError("'timeout' must be a positive number")
95147
elif not self.esema.acquire(0):
96148
raise Empty
97149
self.mutex.acquire()
@@ -115,10 +167,10 @@ def get(self, block=1):
115167
def get_nowait(self):
116168
"""Remove and return an item from the queue without blocking.
117169
118-
Only get an item if one is immediately available. Otherwise
170+
Only get an item if one is immediately available. Otherwise
119171
raise the Empty exception.
120172
"""
121-
return self.get(0)
173+
return self.get(False)
122174

123175
# Override these methods to implement other queue organizations
124176
# (e.g. stack or priority queue).

Lib/test/test_queue.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,23 @@ def FailingQueueTest(q):
6060
raise RuntimeError, "Call this function with an empty queue"
6161
for i in range(queue_size-1):
6262
q.put(i)
63-
q.fail_next_put = True
6463
# Test a failing non-blocking put.
64+
q.fail_next_put = True
6565
try:
6666
q.put("oops", block=0)
6767
raise TestFailed("The queue didn't fail when it should have")
6868
except FailingQueueException:
6969
pass
70+
q.fail_next_put = True
71+
try:
72+
q.put("oops", timeout=0.1)
73+
raise TestFailed("The queue didn't fail when it should have")
74+
except FailingQueueException:
75+
pass
7076
q.put("last")
7177
verify(q.full(), "Queue should be full")
72-
q.fail_next_put = True
7378
# Test a failing blocking put
79+
q.fail_next_put = True
7480
try:
7581
_doBlockingTest( q.put, ("full",), q.get, ())
7682
raise TestFailed("The queue didn't fail when it should have")
@@ -79,6 +85,16 @@ def FailingQueueTest(q):
7985
# Check the Queue isn't damaged.
8086
# put failed, but get succeeded - re-add
8187
q.put("last")
88+
# Test a failing timeout put
89+
q.fail_next_put = True
90+
try:
91+
_doBlockingTest( q.put, ("full", True, 0.2), q.get, ())
92+
raise TestFailed("The queue didn't fail when it should have")
93+
except FailingQueueException:
94+
pass
95+
# Check the Queue isn't damaged.
96+
# put failed, but get succeeded - re-add
97+
q.put("last")
8298
verify(q.full(), "Queue should be full")
8399
q.get()
84100
verify(not q.full(), "Queue should not be full")
@@ -98,6 +114,13 @@ def FailingQueueTest(q):
98114
except FailingQueueException:
99115
pass
100116
verify(not q.empty(), "Queue should not be empty")
117+
q.fail_next_get = True
118+
try:
119+
q.get(timeout=0.1)
120+
raise TestFailed("The queue didn't fail when it should have")
121+
except FailingQueueException:
122+
pass
123+
verify(not q.empty(), "Queue should not be empty")
101124
q.get()
102125
verify(q.empty(), "Queue should be empty")
103126
q.fail_next_get = True
@@ -128,8 +151,14 @@ def SimpleQueueTest(q):
128151
raise TestFailed("Didn't appear to block with a full queue")
129152
except Queue.Full:
130153
pass
154+
try:
155+
q.put("full", timeout=0.1)
156+
raise TestFailed("Didn't appear to time-out with a full queue")
157+
except Queue.Full:
158+
pass
131159
# Test a blocking put
132160
_doBlockingTest( q.put, ("full",), q.get, ())
161+
_doBlockingTest( q.put, ("full", True, 0.2), q.get, ())
133162
# Empty it
134163
for i in range(queue_size):
135164
q.get()
@@ -139,8 +168,14 @@ def SimpleQueueTest(q):
139168
raise TestFailed("Didn't appear to block with an empty queue")
140169
except Queue.Empty:
141170
pass
171+
try:
172+
q.get(timeout=0.1)
173+
raise TestFailed("Didn't appear to time-out with an empty queue")
174+
except Queue.Empty:
175+
pass
142176
# Test a blocking get
143177
_doBlockingTest( q.get, (), q.put, ('empty',))
178+
_doBlockingTest( q.get, (True, 0.2), q.put, ('empty',))
144179

145180
def test():
146181
q=Queue.Queue(queue_size)

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,8 @@ Extension modules
348348
Library
349349
-------
350350

351+
- Queue.Queue.{put,get} now support an optional timeout argument.
352+
351353
- Various features of Tk 8.4 are exposed in Tkinter.py. The multiple
352354
option of tkFileDialog is exposed as function askopenfile{,name}s.
353355

0 commit comments

Comments
 (0)