Add python-eventlet 0.16.1
[packages/trusty/python-eventlet.git] / eventlet / eventlet / pools.py
1 from __future__ import print_function
2
3 import collections
4 from contextlib import contextmanager
5
6 from eventlet import queue
7
8
9 __all__ = ['Pool', 'TokenPool']
10
11
12 class Pool(object):
13     """
14     Pool class implements resource limitation and construction.
15
16     There are two ways of using Pool: passing a `create` argument or
17     subclassing. In either case you must provide a way to create
18     the resource.
19
20     When using `create` argument, pass a function with no arguments::
21
22         http_pool = pools.Pool(create=httplib2.Http)
23
24     If you need to pass arguments, build a nullary function with either
25     `lambda` expression::
26
27         http_pool = pools.Pool(create=lambda: httplib2.Http(timeout=90))
28
29     or :func:`functools.partial`::
30
31         from functools import partial
32         http_pool = pools.Pool(create=partial(httplib2.Http, timeout=90))
33
34     When subclassing, define only the :meth:`create` method
35     to implement the desired resource::
36
37         class MyPool(pools.Pool):
38             def create(self):
39                 return MyObject()
40
41     If using 2.5 or greater, the :meth:`item` method acts as a context manager;
42     that's the best way to use it::
43
44         with mypool.item() as thing:
45             thing.dostuff()
46
47     The maximum size of the pool can be modified at runtime via
48     the :meth:`resize` method.
49
50     Specifying a non-zero *min-size* argument pre-populates the pool with
51     *min_size* items.  *max-size* sets a hard limit to the size of the pool --
52     it cannot contain any more items than *max_size*, and if there are already
53     *max_size* items 'checked out' of the pool, the pool will cause any
54     greenthread calling :meth:`get` to cooperatively yield until an item
55     is :meth:`put` in.
56     """
57
58     def __init__(self, min_size=0, max_size=4, order_as_stack=False, create=None):
59         """*order_as_stack* governs the ordering of the items in the free pool.
60         If ``False`` (the default), the free items collection (of items that
61         were created and were put back in the pool) acts as a round-robin,
62         giving each item approximately equal utilization.  If ``True``, the
63         free pool acts as a FILO stack, which preferentially re-uses items that
64         have most recently been used.
65         """
66         self.min_size = min_size
67         self.max_size = max_size
68         self.order_as_stack = order_as_stack
69         self.current_size = 0
70         self.channel = queue.LightQueue(0)
71         self.free_items = collections.deque()
72         if create is not None:
73             self.create = create
74
75         for x in range(min_size):
76             self.current_size += 1
77             self.free_items.append(self.create())
78
79     def get(self):
80         """Return an item from the pool, when one is available.  This may
81         cause the calling greenthread to block.
82         """
83         if self.free_items:
84             return self.free_items.popleft()
85         self.current_size += 1
86         if self.current_size <= self.max_size:
87             try:
88                 created = self.create()
89             except:
90                 self.current_size -= 1
91                 raise
92             return created
93         self.current_size -= 1  # did not create
94         return self.channel.get()
95
96     @contextmanager
97     def item(self):
98         """ Get an object out of the pool, for use with with statement.
99
100         >>> from eventlet import pools
101         >>> pool = pools.TokenPool(max_size=4)
102         >>> with pool.item() as obj:
103         ...     print("got token")
104         ...
105         got token
106         >>> pool.free()
107         4
108         """
109         obj = self.get()
110         try:
111             yield obj
112         finally:
113             self.put(obj)
114
115     def put(self, item):
116         """Put an item back into the pool, when done.  This may
117         cause the putting greenthread to block.
118         """
119         if self.current_size > self.max_size:
120             self.current_size -= 1
121             return
122
123         if self.waiting():
124             self.channel.put(item)
125         else:
126             if self.order_as_stack:
127                 self.free_items.appendleft(item)
128             else:
129                 self.free_items.append(item)
130
131     def resize(self, new_size):
132         """Resize the pool to *new_size*.
133
134         Adjusting this number does not affect existing items checked out of
135         the pool, nor on any greenthreads who are waiting for an item to free
136         up.  Some indeterminate number of :meth:`get`/:meth:`put`
137         cycles will be necessary before the new maximum size truly matches
138         the actual operation of the pool.
139         """
140         self.max_size = new_size
141
142     def free(self):
143         """Return the number of free items in the pool.  This corresponds
144         to the number of :meth:`get` calls needed to empty the pool.
145         """
146         return len(self.free_items) + self.max_size - self.current_size
147
148     def waiting(self):
149         """Return the number of routines waiting for a pool item.
150         """
151         return max(0, self.channel.getting() - self.channel.putting())
152
153     def create(self):
154         """Generate a new pool item.  In order for the pool to
155         function, either this method must be overriden in a subclass
156         or the pool must be constructed with the `create` argument.
157         It accepts no arguments and returns a single instance of
158         whatever thing the pool is supposed to contain.
159
160         In general, :meth:`create` is called whenever the pool exceeds its
161         previous high-water mark of concurrently-checked-out-items.  In other
162         words, in a new pool with *min_size* of 0, the very first call
163         to :meth:`get` will result in a call to :meth:`create`.  If the first
164         caller calls :meth:`put` before some other caller calls :meth:`get`,
165         then the first item will be returned, and :meth:`create` will not be
166         called a second time.
167         """
168         raise NotImplementedError("Implement in subclass")
169
170
171 class Token(object):
172     pass
173
174
175 class TokenPool(Pool):
176     """A pool which gives out tokens (opaque unique objects), which indicate
177     that the coroutine which holds the token has a right to consume some
178     limited resource.
179     """
180
181     def create(self):
182         return Token()