[pypy-svn] pypy default: (arigo, antocuni): use a weakref to the map in the cache entry, else we indirectly store a strong reference to the class forever

antocuni commits-noreply at bitbucket.org
Tue Feb 15 16:00:50 CET 2011


Author: Antonio Cuni <anto.cuni at gmail.com>
Branch: 
Changeset: r41958:d17fde44288e
Date: 2011-02-15 15:25 +0100
http://bitbucket.org/pypy/pypy/changeset/d17fde44288e/

Log:	(arigo, antocuni): use a weakref to the map in the cache entry, else
	we indirectly store a strong reference to the class forever

diff --git a/pypy/objspace/std/mapdict.py b/pypy/objspace/std/mapdict.py
--- a/pypy/objspace/std/mapdict.py
+++ b/pypy/objspace/std/mapdict.py
@@ -1,3 +1,4 @@
+import weakref
 from pypy.rlib import jit, objectmodel, debug
 from pypy.rlib.rarithmetic import intmask, r_uint
 
@@ -672,7 +673,7 @@
 # Magic caching
 
 class CacheEntry(object):
-    map = None
+    map_wref = None
     version_tag = None
     index = 0
     w_method = None # for callmethod
@@ -684,7 +685,8 @@
         return self.is_valid_for_map(map)
 
     def is_valid_for_map(self, map):
-        if map is self.map:
+        mymap = self.map_wref()
+        if mymap is not None and mymap is map:
             version_tag = map.terminator.w_cls.version_tag()
             if version_tag is self.version_tag:
                 # everything matches, it's incredibly fast
@@ -693,11 +695,11 @@
                 return True
         return False
 
+_invalid_cache_entry_map = objectmodel.instantiate(AbstractAttribute)
+_invalid_cache_entry_map.terminator = None
 INVALID_CACHE_ENTRY = CacheEntry()
-INVALID_CACHE_ENTRY.map = objectmodel.instantiate(AbstractAttribute)
-                             # different from any real map ^^^
-INVALID_CACHE_ENTRY.map.terminator = None
-
+INVALID_CACHE_ENTRY.map_wref = weakref.ref(_invalid_cache_entry_map)
+                                 # different from any real map ^^^
 
 def init_mapdict_cache(pycode):
     num_entries = len(pycode.co_names_w)
@@ -708,7 +710,7 @@
     if entry is INVALID_CACHE_ENTRY:
         entry = CacheEntry()
         pycode._mapdict_caches[nameindex] = entry
-    entry.map = map
+    entry.map_wref = weakref.ref(map)
     entry.version_tag = version_tag
     entry.index = index
     entry.w_method = w_method

diff --git a/pypy/objspace/std/test/test_mapdict.py b/pypy/objspace/std/test/test_mapdict.py
--- a/pypy/objspace/std/test/test_mapdict.py
+++ b/pypy/objspace/std/test/test_mapdict.py
@@ -866,6 +866,22 @@
         res = self.check(f, 'm')
         assert res == (0, 2, 1)
 
+    def test_dont_keep_class_alive(self):
+        import weakref
+        import gc
+        def f():
+            class C(object):
+                def m(self):
+                    pass
+            r = weakref.ref(C)
+            # Trigger cache.
+            C().m()
+            del C
+            gc.collect(); gc.collect(); gc.collect()
+            assert r() is None
+            return 42
+        f() 
+
 class AppTestGlobalCaching(AppTestWithMapDict):
     def setup_class(cls):
         cls.space = gettestobjspace(


More information about the Pypy-commit mailing list