[pypy-commit] pypy stmgc-c8: Change the implementation method of threadlocalproperty().

arigo noreply at buildbot.pypy.org
Tue Jun 23 12:02:18 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: stmgc-c8
Changeset: r78253:d3e128694145
Date: 2015-06-23 12:02 +0200
http://bitbucket.org/pypy/pypy/changeset/d3e128694145/

Log:	Change the implementation method of threadlocalproperty(). Now it
	shouldn't give conflicts the first time the property is accessed on
	a given object.

diff --git a/lib_pypy/_weakkeyiddict.py b/lib_pypy/_weakkeyiddict.py
new file mode 100644
--- /dev/null
+++ b/lib_pypy/_weakkeyiddict.py
@@ -0,0 +1,60 @@
+# Copied and adapted from lib-python/2.7/weakref.py.
+# In PyPy (at least -STM), this is not used: a more
+# efficient version is found in the _weakref module.
+
+import UserDict, weakref
+
+
+class idref(weakref.ref):
+
+    def __eq__(self, other):
+        if self is other:
+            return True
+        if not isinstance(other, idref):
+            return NotImplementedError
+        s = self()
+        o = other()
+        return s is o is not None
+
+    def __hash__(self):
+        try:
+            return self._hash_cache
+        except AttributeError:
+            self._hash_cache = id(self())
+            return self._hash_cache
+
+
+class weakkeyiddict(object):
+    """ Mapping class that references keys weakly.
+    In addition, uses the identity of the keys, rather than the equality.
+    (Only a subset of the dict interface is available.)
+    """
+
+    def __init__(self):
+        self.data = {}
+        def remove(k, selfref=weakref.ref(self)):
+            self = selfref()
+            if self is not None:
+                del self.data[k]
+        self._remove = remove
+
+    def __delitem__(self, key):
+        del self.data[idref(key)]
+
+    def __getitem__(self, key):
+        return self.data[idref(key)]
+
+    def __setitem__(self, key, value):
+        self.data[idref(key, self._remove)] = value
+
+    def get(self, key, default=None):
+        return self.data.get(idref(key),default)
+
+    def __contains__(self, key):
+        return idref(key) in self.data
+
+    def pop(self, key, *args):
+        return self.data.pop(idref(key), *args)
+
+    def setdefault(self, key, default=None):
+        return self.data.setdefault(idref(key, self._remove),default)
diff --git a/lib_pypy/transaction.py b/lib_pypy/transaction.py
--- a/lib_pypy/transaction.py
+++ b/lib_pypy/transaction.py
@@ -52,6 +52,12 @@
     from time import time, clock
 
 try:
+    from _weakref import weakkeyiddict
+except ImportError:
+    # Not a STM-enabled PyPy.
+    from _weakkeyiddict import weakkeyiddict
+
+try:
     from pypystm import queue, Empty
 except ImportError:
     from Queue import Queue as queue
@@ -232,29 +238,32 @@
 class threadlocalproperty(object):
     def __init__(self, default_factory=None):
         self.tl_default_factory = default_factory
-        self.tl_name = intern('tlprop.%d' % id(self))
+        self.tl_local = thread._local()
 
-    def tl_get(self, obj):
+    def tl_wrefs(self):
         try:
-            return obj._threadlocalproperties
+            return self.tl_local.wrefs
         except AttributeError:
-            return obj.__dict__.setdefault('_threadlocalproperties',
-                                           thread._local())
+            self.tl_local.wrefs = wrefs = weakkeyiddict()
+            return wrefs
 
     def __get__(self, obj, cls=None):
         if obj is None:
             return self
+        wrefs = self.tl_wrefs()
         try:
-            return getattr(self.tl_get(obj), self.tl_name)
-        except AttributeError:
+            return wrefs[obj]
+        except KeyError:
             if self.tl_default_factory is None:
-                raise
-            result = self.tl_default_factory()
-            setattr(self.tl_get(obj), self.tl_name, result)
+                raise AttributeError
+            wrefs[obj] = result = self.tl_default_factory()
             return result
 
     def __set__(self, obj, value):
-        setattr(self.tl_get(obj), self.tl_name, value)
+        self.tl_wrefs()[obj] = value
 
     def __delete__(self, obj):
-        delattr(self.tl_get(obj), self.tl_name)
+        try:
+            del self.tl_wrefs()[obj]
+        except KeyError:
+            raise AttributeError


More information about the pypy-commit mailing list