PageRenderTime 41ms CodeModel.GetById 21ms app.highlight 16ms RepoModel.GetById 0ms app.codeStats 0ms

/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()