[pypy-svn] r47611 - in pypy/dist/pypy/rpython/memory: gc test

arigo at codespeak.net arigo at codespeak.net
Fri Oct 19 22:01:26 CEST 2007


Author: arigo
Date: Fri Oct 19 22:01:25 2007
New Revision: 47611

Modified:
   pypy/dist/pypy/rpython/memory/gc/generation.py
   pypy/dist/pypy/rpython/memory/test/test_gc.py
   pypy/dist/pypy/rpython/memory/test/test_transformed_gc.py
Log:
(cfbolz, arigo)  Fix weakrefs for the GenerationGC.


Modified: pypy/dist/pypy/rpython/memory/gc/generation.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/gc/generation.py	(original)
+++ pypy/dist/pypy/rpython/memory/gc/generation.py	Fri Oct 19 22:01:25 2007
@@ -34,6 +34,7 @@
         self.reset_nursery()
         self.old_objects_pointing_to_young = nonnull_endmarker
         # ^^^ the head of a linked list inside the old objects space
+        self.young_objects_with_weakrefs = self.AddressLinkedList()
 
     def reset_nursery(self):
         self.nursery      = llmemory.NULL
@@ -45,8 +46,9 @@
 
     def malloc_fixedsize(self, typeid, size, can_collect, has_finalizer=False,
                          contains_weakptr=False):
-        if (has_finalizer or contains_weakptr or not can_collect or
+        if (has_finalizer or not can_collect or
             raw_malloc_usage(size) >= self.nursery_size // 2):
+            debug_assert(not contains_weakptr, "wrong case for mallocing weakref")
             # "non-simple" case or object too big: don't use the nursery
             return SemiSpaceGC.malloc_fixedsize(self, typeid, size,
                                                 can_collect, has_finalizer,
@@ -59,6 +61,8 @@
         llarena.arena_reserve(result, totalsize)
         self.init_gc_object(result, typeid)
         self.nursery_free = result + totalsize
+        if contains_weakptr:
+            self.young_objects_with_weakrefs.append(result + size_gc_header)
         return llmemory.cast_adr_to_ptr(result+size_gc_header, llmemory.GCREF)
 
     def malloc_varsize(self, typeid, length, size, itemsize, offset_to_length,
@@ -86,6 +90,7 @@
 
     def semispace_collect(self, size_changing=False):
         self.reset_forwarding() # we are doing a full collection anyway
+        self.weakrefs_grow_older()
         self.reset_nursery()
         SemiSpaceGC.semispace_collect(self, size_changing)
 
@@ -97,6 +102,11 @@
             hdr.forw = llmemory.NULL
         self.old_objects_pointing_to_young = nonnull_endmarker
 
+    def weakrefs_grow_older(self):
+        while self.young_objects_with_weakrefs.non_empty():
+            obj = self.young_objects_with_weakrefs.pop()
+            self.objects_with_weakrefs.append(obj)
+
     def collect_nursery(self):
         if self.nursery_size > self.top_of_space - self.free:
             # the semispace is running out, do a full collect
@@ -109,6 +119,8 @@
             self.collect_oldrefs_to_nursery()
             self.collect_roots_in_nursery()
             self.scan_objects_just_copied_out_of_nursery(scan)
+            if self.young_objects_with_weakrefs.non_empty():
+                self.invalidate_young_weakrefs()
             self.notify_objects_just_moved()
             # mark the nursery as free and fill it with zeroes again
             llarena.arena_reset(self.nursery, self.nursery_size, True)
@@ -185,6 +197,25 @@
                     j += 1
                 i += 1
 
+    def invalidate_young_weakrefs(self):
+        # walk over the list of objects that contain weakrefs and are in the
+        # nursery.  if the object it references survives then update the
+        # weakref; otherwise invalidate the weakref
+        while self.young_objects_with_weakrefs.non_empty():
+            obj = self.young_objects_with_weakrefs.pop()
+            if not self.is_forwarded(obj):
+                continue # weakref itself dies
+            obj = self.get_forwarding_address(obj)
+            offset = self.weakpointer_offset(self.header(obj).typeid)
+            pointing_to = (obj + offset).address[0]
+            if self.is_in_nursery(pointing_to):
+                if self.is_forwarded(pointing_to):
+                    (obj + offset).address[0] = self.get_forwarding_address(
+                        pointing_to)
+                    self.objects_with_weakrefs.append(obj)
+                else:
+                    (obj + offset).address[0] = NULL
+
     def write_barrier(self, addr, addr_to, addr_struct):
         if not self.is_in_nursery(addr_struct) and self.is_in_nursery(addr):
             oldhdr = self.header(addr_struct)

Modified: pypy/dist/pypy/rpython/memory/test/test_gc.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/test/test_gc.py	(original)
+++ pypy/dist/pypy/rpython/memory/test/test_gc.py	Fri Oct 19 22:01:25 2007
@@ -307,6 +307,25 @@
         res = self.interpret(f, [500])
         assert res == 1 + 500
 
+    def test_weakref_across_minor_collection(self):
+        import weakref
+        class A:
+            pass
+        def f(x):
+            a = A()
+            a.foo = x
+            ref = weakref.ref(a)
+            all = [None] * x
+            i = 0
+            while i < x:
+                all[i] = [i] * i
+                i += 1
+            assert ref() is a
+            llop.gc__collect(lltype.Void)
+            assert ref() is a
+            return a.foo + len(all)
+        res = self.interpret(f, [20])  # for GenerationGC, enough for a minor collection
+        assert res == 20 + 20
 
 class TestMarkSweepGC(GCTest):
     from pypy.rpython.memory.gc.marksweep import MarkSweepGC as GCClass

Modified: pypy/dist/pypy/rpython/memory/test/test_transformed_gc.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/test/test_transformed_gc.py	(original)
+++ pypy/dist/pypy/rpython/memory/test/test_transformed_gc.py	Fri Oct 19 22:01:25 2007
@@ -736,3 +736,25 @@
             GC_PARAMS = {'space_size': 2048,
                          'nursery_size': 128}
             root_stack_depth = 200
+
+    def test_weakref_across_minor_collection(self):
+        import weakref
+        class A:
+            pass
+        def f():
+            x = 20    # for GenerationGC, enough for a minor collection
+            a = A()
+            a.foo = x
+            ref = weakref.ref(a)
+            all = [None] * x
+            i = 0
+            while i < x:
+                all[i] = [i] * i
+                i += 1
+            assert ref() is a
+            llop.gc__collect(lltype.Void)
+            assert ref() is a
+            return a.foo + len(all)
+        run = self.runner(f)
+        res = run([])
+        assert res == 20 + 20



More information about the Pypy-commit mailing list