Improved docs on pools for ericflo, fixes #40.

This commit is contained in:
Ryan Williams
2010-02-21 22:31:23 -05:00
parent 2466add6fa
commit 13f199da00

View File

@@ -34,35 +34,41 @@ except ImportError:
class Pool(object): class Pool(object):
""" """
Pool is a base class that is meant to be subclassed. When subclassing, Pool is a base class that implements resource limitation and construction.
define the :meth:`create` method to implement the desired resource. It is meant to be subclassed. When subclassing, define only
the :meth:`create` method to implement the desired resource::
When using the pool, if you do a get, you should **always** do a class MyPool(pools.Pool):
:meth:`put`. def create(self):
return MyObject()
The pattern is:: If using 2.5 or greater, the :meth:`item` method acts as a context manager;
that's the best way to use it::
thing = self.pool.get() with mypool.item() as thing:
try: thing.dostuff()
thing.method()
finally:
self.pool.put(thing)
The maximum size of the pool can be modified at runtime via the If stuck on 2.4, the :meth:`get` and :meth:`put` methods are the preferred
:attr:`max_size` attribute. Adjusting this number does not affect existing nomenclature. Use a ``finally`` to ensure that nothing is leaked::
items checked out of the pool, nor on any waiters who are waiting for an
item to free up. Some indeterminate number of :meth:`get`/:meth:`put` thing = self.pool.get()
cycles will be necessary before the new maximum size truly matches the try:
actual operation of the pool. thing.dostuff()
finally:
self.pool.put(thing)
The maximum size of the pool can be modified at runtime via
the :meth:`resize` method.
Specifying a non-zero *min-size* argument pre-populates the pool with
*min_size* items. *max-size* sets a hard limit to the size of the pool --
it cannot contain any more items than *max_size*, and if there are already
*max_size* items 'checked out' of the pool, the pool will cause any
greenthread calling :meth:`get` to cooperatively yield until an item
is :meth:`put` in.
""" """
def __init__(self, min_size=0, max_size=4, order_as_stack=False): def __init__(self, min_size=0, max_size=4, order_as_stack=False):
""" Pre-populates the pool with *min_size* items. Sets a hard limit to """*order_as_stack* governs the ordering of the items in the free pool.
the size of the pool -- it cannot contain any more items than
*max_size*, and if there are already *max_size* items 'checked out' of
the pool, the pool will cause any getter to cooperatively yield until an
item is put in.
*order_as_stack* governs the ordering of the items in the free pool.
If ``False`` (the default), the free items collection (of items that If ``False`` (the default), the free items collection (of items that
were created and were put back in the pool) acts as a round-robin, were created and were put back in the pool) acts as a round-robin,
giving each item approximately equal utilization. If ``True``, the giving each item approximately equal utilization. If ``True``, the
@@ -80,7 +86,8 @@ class Pool(object):
self.free_items.append(self.create()) self.free_items.append(self.create())
def get(self): def get(self):
"""Return an item from the pool, when one is available """Return an item from the pool, when one is available. This may
cause the calling greenthread to block.
""" """
if self.free_items: if self.free_items:
return self.free_items.popleft() return self.free_items.popleft()
@@ -94,7 +101,8 @@ class Pool(object):
item = item_impl item = item_impl
def put(self, item): def put(self, item):
"""Put an item back into the pool, when done """Put an item back into the pool, when done. This may
cause the putting greenthread to block.
""" """
if self.current_size > self.max_size: if self.current_size > self.max_size:
self.current_size -= 1 self.current_size -= 1
@@ -109,12 +117,19 @@ class Pool(object):
self.free_items.append(item) self.free_items.append(item)
def resize(self, new_size): def resize(self, new_size):
"""Resize the pool """Resize the pool to *new_size*.
Adjusting this number does not affect existing items checked out of
the pool, nor on any greenthreads who are waiting for an item to free
up. Some indeterminate number of :meth:`get`/:meth:`put`
cycles will be necessary before the new maximum size truly matches
the actual operation of the pool.
""" """
self.max_size = new_size self.max_size = new_size
def free(self): def free(self):
"""Return the number of free items in the pool. """Return the number of free items in the pool. This corresponds
to the number of :meth:`get` calls needed to empty the pool.
""" """
return len(self.free_items) + self.max_size - self.current_size return len(self.free_items) + self.max_size - self.current_size
@@ -124,7 +139,17 @@ class Pool(object):
return max(0, self.channel.getting() - self.channel.putting()) return max(0, self.channel.getting() - self.channel.putting())
def create(self): def create(self):
"""Generate a new pool item """Generate a new pool item. This method must be overridden in order
for the pool to function. It accepts no arguments and returns a single
instance of whatever thing the pool is supposed to contain.
In general, :meth:`create` is called whenever the pool exceeds its
previous high-water mark of concurrently-checked-out-items. In other
words, in a new pool with *min_size* of 0, the very first call
to :meth:`get` will result in a call to :meth:`create`. If the first
caller calls :meth:`put` before some other caller calls :meth:`get`,
then the first item will be returned, and :meth:`create` will not be
called a second time.
""" """
raise NotImplementedError("Implement in subclass") raise NotImplementedError("Implement in subclass")