summaryrefslogtreecommitdiff
path: root/dogpile/cache/util.py
diff options
context:
space:
mode:
Diffstat (limited to 'dogpile/cache/util.py')
-rw-r--r--dogpile/cache/util.py54
1 files changed, 50 insertions, 4 deletions
diff --git a/dogpile/cache/util.py b/dogpile/cache/util.py
index 47f7bbe..8122565 100644
--- a/dogpile/cache/util.py
+++ b/dogpile/cache/util.py
@@ -2,6 +2,7 @@ from hashlib import sha1
import inspect
import sys
import re
+import collections
try:
import threading
@@ -73,13 +74,13 @@ def function_key_generator(namespace, fn):
"""Return a function that generates a string
key, based on a given function as well as
arguments to the returned function itself.
-
+
This is used by :meth:`.CacheRegion.cache_on_arguments`
to generate a cache key from a decorated function.
-
+
It can be replaced using the ``function_key_generator``
argument passed to :func:`.make_region`.
-
+
"""
if namespace is None:
@@ -107,7 +108,7 @@ def sha1_mangle_key(key):
def length_conditional_mangler(length, mangler):
"""a key mangler that mangles if the length of the key is
past a certain threshold.
-
+
"""
def mangle(key):
if len(key) >= length:
@@ -137,3 +138,48 @@ def to_list(x, default=None):
return [x]
else:
return x
+
+
+class KeyReentrantMutex(object):
+
+ def __init__(self, key, mutex, keys):
+ self.key = key
+ self.mutex = mutex
+ self.keys = keys
+
+ @classmethod
+ def factory(cls, mutex):
+ # this collection holds zero or one
+ # thread idents as the key; a set of
+ # keynames held as the value.
+ keystore = collections.defaultdict(set)
+ def fac(key):
+ return KeyReentrantMutex(key, mutex, keystore)
+ return fac
+
+ def acquire(self, wait=True):
+ current_thread = threading.current_thread().ident
+ keys = self.keys.get(current_thread)
+ if keys is not None and \
+ self.key not in keys:
+ # current lockholder, new key. add it in
+ keys.add(self.key)
+ return True
+ elif self.mutex.acquire(wait=wait):
+ # after acquire, create new set and add our key
+ self.keys[current_thread].add(self.key)
+ return True
+ else:
+ return False
+
+ def release(self):
+ current_thread = threading.current_thread().ident
+ keys = self.keys.get(current_thread)
+ assert keys is not None, "this thread didn't do the acquire"
+ assert self.key in keys, "No acquire held for key '%s'" % self.key
+ keys.remove(self.key)
+ if not keys:
+ # when list of keys empty, remove
+ # the thread ident and unlock.
+ del self.keys[current_thread]
+ self.mutex.release()