/SQLAlchemy-0.7.8/lib/sqlalchemy/util/queue.py
Python | 191 lines | 184 code | 2 blank | 5 comment | 1 complexity | 96e0ec6280b7230db1fa34b924964129 MD5 | raw file
1# util/queue.py
2# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
3#
4# This module is part of SQLAlchemy and is released under
5# the MIT License: http://www.opensource.org/licenses/mit-license.php
6
7"""An adaptation of Py2.3/2.4's Queue module which supports reentrant
8behavior, using RLock instead of Lock for its mutex object.
9
10This is to support the connection pool's usage of weakref callbacks to return
11connections to the underlying Queue, which can in extremely
12rare cases be invoked within the ``get()`` method of the Queue itself,
13producing a ``put()`` inside the ``get()`` and therefore a reentrant
14condition."""
15
16from collections import deque
17from time import time as _time
18from sqlalchemy.util import threading
19
20__all__ = ['Empty', 'Full', 'Queue']
21
22class Empty(Exception):
23 "Exception raised by Queue.get(block=0)/get_nowait()."
24
25 pass
26
27class Full(Exception):
28 "Exception raised by Queue.put(block=0)/put_nowait()."
29
30 pass
31
32class Queue:
33 def __init__(self, maxsize=0):
34 """Initialize a queue object with a given maximum size.
35
36 If `maxsize` is <= 0, the queue size is infinite.
37 """
38
39 self._init(maxsize)
40 # mutex must be held whenever the queue is mutating. All methods
41 # that acquire mutex must release it before returning. mutex
42 # is shared between the two conditions, so acquiring and
43 # releasing the conditions also acquires and releases mutex.
44 self.mutex = threading.RLock()
45 # Notify not_empty whenever an item is added to the queue; a
46 # thread waiting to get is notified then.
47 self.not_empty = threading.Condition(self.mutex)
48 # Notify not_full whenever an item is removed from the queue;
49 # a thread waiting to put is notified then.
50 self.not_full = threading.Condition(self.mutex)
51
52 def qsize(self):
53 """Return the approximate size of the queue (not reliable!)."""
54
55 self.mutex.acquire()
56 n = self._qsize()
57 self.mutex.release()
58 return n
59
60 def empty(self):
61 """Return True if the queue is empty, False otherwise (not
62 reliable!)."""
63
64 self.mutex.acquire()
65 n = self._empty()
66 self.mutex.release()
67 return n
68
69 def full(self):
70 """Return True if the queue is full, False otherwise (not
71 reliable!)."""
72
73 self.mutex.acquire()
74 n = self._full()
75 self.mutex.release()
76 return n
77
78 def put(self, item, block=True, timeout=None):
79 """Put an item into the queue.
80
81 If optional args `block` is True and `timeout` is None (the
82 default), block if necessary until a free slot is
83 available. If `timeout` is a positive number, it blocks at
84 most `timeout` seconds and raises the ``Full`` exception if no
85 free slot was available within that time. Otherwise (`block`
86 is false), put an item on the queue if a free slot is
87 immediately available, else raise the ``Full`` exception
88 (`timeout` is ignored in that case).
89 """
90
91 self.not_full.acquire()
92 try:
93 if not block:
94 if self._full():
95 raise Full
96 elif timeout is None:
97 while self._full():
98 self.not_full.wait()
99 else:
100 if timeout < 0:
101 raise ValueError("'timeout' must be a positive number")
102 endtime = _time() + timeout
103 while self._full():
104 remaining = endtime - _time()
105 if remaining <= 0.0:
106 raise Full
107 self.not_full.wait(remaining)
108 self._put(item)
109 self.not_empty.notify()
110 finally:
111 self.not_full.release()
112
113 def put_nowait(self, item):
114 """Put an item into the queue without blocking.
115
116 Only enqueue the item if a free slot is immediately available.
117 Otherwise raise the ``Full`` exception.
118 """
119 return self.put(item, False)
120
121 def get(self, block=True, timeout=None):
122 """Remove and return an item from the queue.
123
124 If optional args `block` is True and `timeout` is None (the
125 default), block if necessary until an item is available. If
126 `timeout` is a positive number, it blocks at most `timeout`
127 seconds and raises the ``Empty`` exception if no item was
128 available within that time. Otherwise (`block` is false),
129 return an item if one is immediately available, else raise the
130 ``Empty`` exception (`timeout` is ignored in that case).
131 """
132
133 self.not_empty.acquire()
134 try:
135 if not block:
136 if self._empty():
137 raise Empty
138 elif timeout is None:
139 while self._empty():
140 self.not_empty.wait()
141 else:
142 if timeout < 0:
143 raise ValueError("'timeout' must be a positive number")
144 endtime = _time() + timeout
145 while self._empty():
146 remaining = endtime - _time()
147 if remaining <= 0.0:
148 raise Empty
149 self.not_empty.wait(remaining)
150 item = self._get()
151 self.not_full.notify()
152 return item
153 finally:
154 self.not_empty.release()
155
156 def get_nowait(self):
157 """Remove and return an item from the queue without blocking.
158
159 Only get an item if one is immediately available. Otherwise
160 raise the ``Empty`` exception.
161 """
162
163 return self.get(False)
164
165 # Override these methods to implement other queue organizations
166 # (e.g. stack or priority queue).
167 # These will only be called with appropriate locks held
168
169 # Initialize the queue representation
170 def _init(self, maxsize):
171 self.maxsize = maxsize
172 self.queue = deque()
173
174 def _qsize(self):
175 return len(self.queue)
176
177 # Check whether the queue is empty
178 def _empty(self):
179 return not self.queue
180
181 # Check whether the queue is full
182 def _full(self):
183 return self.maxsize > 0 and len(self.queue) == self.maxsize
184
185 # Put a new item in the queue
186 def _put(self, item):
187 self.queue.append(item)
188
189 # Get an item from the queue
190 def _get(self):
191 return self.queue.popleft()