Commits

wooparadog committed 6a4f7d5

Refactor `Lock._enter_create`, add various exception handling.

0. Refactor `Lock._enter_create`.
1. Add `NeedAsyncRegenerationException`, which would trigger an async
regeneration. It also hold an `existing_value` for results generated
at the moment.
2. Add `LockReleaseException`, wraps any exception raised by backend
when releasing lock. It also contains results that would be returned
if no exception is raised.

  • Participants
  • Parent commits e9011d8

Comments (0)

Files changed (1)

File dogpile/core/dogpile.py

 
     """
 
+class NeedAsyncRegenerationException(NeedRegenerationException):
+    """Like the `NeedRegenerationException`, but value will be generated
+    asynchronously.
+
+    """
+
+    def __init__(self, *args, **kwds):
+        super(NeedAsyncRegenerationException, self).__init__(*args, **kwds)
+        self.existing_value = None
+
+class LockReleaseException(Exception):
+    """An exception that wrapps the actual exception raised by backend
+    when failing to release a lock.
+
+    :param exc: the actually exception raised by backend
+    :param return_val: returned valued of current call
+    """
+
+    def __init__(self, exc, return_val):
+        self.exc = exc
+        self.return_val = return_val
+
+    def __repr__(self):
+        return "%r: %r" % (self.__class__.__name__, self.exc)
+
+    __str__ = __repr__
+
 NOT_REGENERATED = object()
 
 class Lock(object):
             self.mutex.acquire()
 
         try:
+            return_val = None
             log.debug("value creation lock %r acquired" % self.mutex)
 
             # see if someone created the value already
             try:
                 value, createdtime = self.value_and_created_fn()
+                if self._is_expired(createdtime):
+                    if self.async_creator:
+                        exc = NeedAsyncRegenerationException()
+                        exc.existing_value = (value, createdtime)
+                        raise exc
+                    raise NeedRegenerationException
+
+                log.debug("value already present")
+                return_val = value, createdtime
+
+            except NeedAsyncRegenerationException as e:
+                log.debug("Passing creation lock to async runner")
+                self.async_creator(self.mutex)
+                async = True
+                if e.existing_value is not None:
+                    return_val = e.existing_value
+
             except NeedRegenerationException:
-                pass
-            else:
-                if not self._is_expired(createdtime):
-                    log.debug("value already present")
-                    return value, createdtime
-                elif self.async_creator:
-                    log.debug("Passing creation lock to async runner")
-                    self.async_creator(self.mutex)
-                    async = True
-                    return value, createdtime
-
-            log.debug("Calling creation function")
-            created = self.creator()
-            return created
+                log.debug("Calling creation function")
+                return_val = self.creator()
+
+            return return_val
+
         finally:
             if not async:
-                self.mutex.release()
-                log.debug("Released creation lock")
-
+                try:
+                    self.mutex.release()
+                    log.debug("Released creation lock")
+                except Exception as e:
+                    raise LockReleaseException(e, return_val)
 
     def __enter__(self):
         return self._enter()