durusmail: durus-users: Re: multithread and implicit cache management
RELEASED: Durus-3.5
2006-08-16
2006-09-21
multithread and implicit cache management (was: Re: [Durus-users] RELEASED: Durus-3.5)
2006-09-21
Re: multithread and implicit cache management
2006-09-21
2006-09-22
2006-09-22
2006-09-22
2006-09-26
2006-09-21
2006-09-21
2006-09-22
2006-09-22
Re: RELEASED: Durus-3.5
2006-09-21
Re: multithread and implicit cache management
David Binger
2006-09-22
We do have a risk with WeakValueDict because callbacks can
happen at bad times.  While this is a bigger problem for your
wild applications, I think it can also cause trouble in a
normal application, since gc can cause callbacks from another
thread.

Here is a variation of the solution you suggested for
just holding a plain dict of weakrefs.  Please review it.


===================================================================
--- connection.py       (revision 28839)
+++ connection.py       (working copy)
@@ -14,7 +14,7 @@
from itertools import islice, chain
from os import getpid
from time import time
-from weakref import WeakValueDictionary, ref
+from weakref import ref, KeyedRef
ROOT_OID = p64(0)
@@ -306,11 +306,48 @@
          self.abort()
          self.storage.pack()

+class ObjectDictionary (object):

+    def __init__(self):
+        self.mapping = {}
+        self.dead = set()
+        def remove(wr, selfref=ref(self)):
+            self = selfref()
+            if self is not None:
+                self.dead.add(wr.key)
+        self.remove = remove
+
+    def get(self, key, default=None):
+        ref = self.mapping.get(key, None)
+        if ref is not None:
+            value = ref()
+            if value is not None and key not in self.dead:
+                return value
+        return default
+
+    def __setitem__(self, key, value):
+        self.mapping[key] = KeyedRef(value, self.remove, key)
+
+    def __delitem__(self, key):
+        self.dead.add(key)
+
+    def __contains__(self, key):
+        return self.get(key, None) is not None
+
+    def __len__(self):
+        return len(self.mapping) - len(self.dead)
+
+    def __iter__(self):
+        while self.dead:
+            self.mapping.pop(self.dead.pop(), None)
+        for key in self.mapping:
+            if key not in self.dead:
+                yield key
+
+
class Cache(object):
      def __init__(self, size):
-        self.objects = WeakValueDictionary()
+        self.objects = ObjectDictionary()
          self.recent_objects = set()
          self.set_size(size)
          self.finger = 0

@@ -373,7 +373,9 @@
          heap = []
          for oid in islice(chain(all, all), start, start + len(all)):
              self.finger += 1
-            obj = all[oid]
+            obj = all.get(oid)
+            if obj is None:
+                continue # The ref is dead.
              if obj._p_serial == transaction_serial:
                  continue # obj is current.  Leave it alone.
              heappush(heap, (obj._p_serial, oid))

reply