pools.py 6.3 KB

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