summaryrefslogtreecommitdiff
path: root/pymemcache/pool.py
diff options
context:
space:
mode:
Diffstat (limited to 'pymemcache/pool.py')
-rw-r--r--pymemcache/pool.py110
1 files changed, 110 insertions, 0 deletions
diff --git a/pymemcache/pool.py b/pymemcache/pool.py
new file mode 100644
index 0000000..c341ceb
--- /dev/null
+++ b/pymemcache/pool.py
@@ -0,0 +1,110 @@
+# Copyright 2015 Yahoo.com
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import collections
+import contextlib
+import sys
+import threading
+
+import six
+
+
+class ObjectPool(object):
+ """A pool of objects that release/creates/destroys as needed."""
+
+ def __init__(self, obj_creator,
+ after_remove=None, max_size=None):
+ self._used_objs = collections.deque()
+ self._free_objs = collections.deque()
+ self._obj_creator = obj_creator
+ self._lock = threading.Lock()
+ self._after_remove = after_remove
+ max_size = max_size or 2 ** 31
+ if not isinstance(max_size, six.integer_types) or max_size < 0:
+ raise ValueError('"max_size" must be a positive integer')
+ self.max_size = max_size
+
+ @property
+ def used(self):
+ return tuple(self._used_objs)
+
+ @property
+ def free(self):
+ return tuple(self._free_objs)
+
+ @contextlib.contextmanager
+ def get_and_release(self, destroy_on_fail=False):
+ obj = self.get()
+ try:
+ yield obj
+ except Exception:
+ exc_info = sys.exc_info()
+ if not destroy_on_fail:
+ self.release(obj)
+ else:
+ self.destroy(obj)
+ six.reraise(exc_info[0], exc_info[1], exc_info[2])
+ self.release(obj)
+
+ def get(self):
+ with self._lock:
+ if not self._free_objs:
+ curr_count = len(self._used_objs)
+ if curr_count >= self.max_size:
+ raise RuntimeError("Too many objects,"
+ " %s >= %s" % (curr_count,
+ self.max_size))
+ obj = self._obj_creator()
+ self._used_objs.append(obj)
+ return obj
+ else:
+ obj = self._free_objs.pop()
+ self._used_objs.append(obj)
+ return obj
+
+ def destroy(self, obj, silent=True):
+ was_dropped = False
+ with self._lock:
+ try:
+ self._used_objs.remove(obj)
+ was_dropped = True
+ except ValueError:
+ if not silent:
+ raise
+ if was_dropped and self._after_remove is not None:
+ self._after_remove(obj)
+
+ def release(self, obj, silent=True):
+ with self._lock:
+ try:
+ self._used_objs.remove(obj)
+ self._free_objs.append(obj)
+ except ValueError:
+ if not silent:
+ raise
+
+ def clear(self):
+ if self._after_remove is not None:
+ needs_destroy = []
+ with self._lock:
+ needs_destroy.extend(self._used_objs)
+ needs_destroy.extend(self._free_objs)
+ self._free_objs.clear()
+ self._used_objs.clear()
+ for obj in needs_destroy:
+ self._after_remove(obj)
+ else:
+ with self._lock:
+ self._free_objs.clear()
+ self._used_objs.clear()