replace task.LoopingCall's with deferral.RobustLoopingCall that catches errors and...
[p2pool.git] / p2pool / util / expiring_dict.py
index 180cd42..8a3c9ee 100644 (file)
@@ -1,8 +1,9 @@
 from __future__ import division
 
 import time
+import weakref
 
-from twisted.internet import reactor
+from p2pool.util import deferral
 
 class Node(object):
     def __init__(self, contents, prev=None, next=None):
@@ -61,7 +62,6 @@ class LinkedList(object):
             yield cur2
     
     def __getitem__(self, index):
-        # odd one out - probably should return Node instance instead of its contents
         if index < 0:
             cur = self.end
             for i in xrange(-index):
@@ -98,19 +98,24 @@ class LinkedList(object):
 
 
 class ExpiringDict(object):
-    def __init__(self, expiry_time=100, get_touches=True):
+    def __init__(self, expiry_time, get_touches=True):
         self.expiry_time = expiry_time
         self.get_touches = get_touches
         
         self.expiry_deque = LinkedList()
         self.d = dict() # key -> node, value
+        
+        self_ref = weakref.ref(self, lambda _: expire_loop.stop() if expire_loop.running else None)
+        self._expire_loop = expire_loop = deferral.RobustLoopingCall(lambda: self_ref().expire())
+        expire_loop.start(1)
+    
+    def stop(self):
+        self._expire_loop.stop()
     
     def __repr__(self):
-        reactor.callLater(0, self.expire)
         return 'ExpiringDict' + repr(self.__dict__)
     
     def __len__(self):
-        reactor.callLater(0, self.expire)
         return len(self.d)
     
     _nothing = object()
@@ -121,13 +126,14 @@ class ExpiringDict(object):
             node.delete()
         
         new_value = old_value if value is self._nothing else value
-        self.d[key] = self.expiry_deque.append((time.time(), key)), new_value
+        self.d[key] = self.expiry_deque.append((time.time() + self.expiry_time, key)), new_value
         return new_value
     
     def expire(self):
+        t = time.time()
         for node in self.expiry_deque:
             timestamp, key = node.contents
-            if timestamp + self.expiry_time > time.time():
+            if timestamp > t:
                 break
             del self.d[key]
             node.delete()
@@ -140,24 +146,20 @@ class ExpiringDict(object):
             value = self.touch(key)
         else:
             node, value = self.d[key]
-        reactor.callLater(0, self.expire)
         return value
     
     def __setitem__(self, key, value):
         self.touch(key, value)
-        reactor.callLater(0, self.expire)
     
     def __delitem__(self, key):
         node, value = self.d.pop(key)
         node.delete()
-        reactor.callLater(0, self.expire)
     
     def get(self, key, default_value=None):
         if key in self.d:
             res = self[key]
         else:
             res = default_value
-            reactor.callLater(0, self.expire)
         return res
     
     def setdefault(self, key, default_value):
@@ -176,23 +178,3 @@ class ExpiringDict(object):
     def itervalues(self):
         for node, value in self.d.itervalues():
             yield value
-
-if __name__ == '__main__':
-    x = ExpiringDict(5)
-    print x
-    
-    time.sleep(1)
-    
-    x[1] = 2
-    print 'x[1] = 2'
-    print x
-    
-    time.sleep(1)
-    
-    x[1] = 3
-    print 'x[1] = 3'
-    print x
-    
-    time.sleep(5)
-    
-    print x