[pypy-commit] pypy stm-gc: Weakrefs again.

arigo noreply at buildbot.pypy.org
Wed Apr 18 18:07:41 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: stm-gc
Changeset: r54502:7f73ab377959
Date: 2012-04-18 16:50 +0200
http://bitbucket.org/pypy/pypy/changeset/7f73ab377959/

Log:	Weakrefs again.

diff --git a/pypy/rpython/memory/gc/stmgc.py b/pypy/rpython/memory/gc/stmgc.py
--- a/pypy/rpython/memory/gc/stmgc.py
+++ b/pypy/rpython/memory/gc/stmgc.py
@@ -42,8 +42,7 @@
 GCFLAG_WAS_COPIED = first_gcflag << 1     # keep in sync with et.c
 GCFLAG_HAS_SHADOW = first_gcflag << 2
 GCFLAG_FIXED_HASH = first_gcflag << 3
-GCFLAG_WEAKREF    = first_gcflag << 4
-GCFLAG_VISITED    = first_gcflag << 5
+GCFLAG_VISITED    = first_gcflag << 4
 
 
 def always_inline(fn):
@@ -161,15 +160,15 @@
         # Get the memory from the nursery.
         size_gc_header = self.gcheaderbuilder.size_gc_header
         totalsize = size_gc_header + size
-        result = self.get_tls().allocate_bump_pointer(totalsize)
+        tls = self.get_tls()
+        result = tls.allocate_bump_pointer(totalsize)
         #
         # Build the object.
         llarena.arena_reserve(result, totalsize)
         obj = result + size_gc_header
-        flags = 0
+        self.init_gc_object(result, typeid, flags=0)
         if contains_weakptr:   # check constant-folded
-            flags |= GCFLAG_WEAKREF
-        self.init_gc_object(result, typeid, flags=flags)
+            tls.fresh_new_weakref(obj)
         #
         return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
 
diff --git a/pypy/rpython/memory/gc/stmshared.py b/pypy/rpython/memory/gc/stmshared.py
--- a/pypy/rpython/memory/gc/stmshared.py
+++ b/pypy/rpython/memory/gc/stmshared.py
@@ -43,7 +43,7 @@
         adr1 = adr2 - self.gc.gcheaderbuilder.size_gc_header
         llarena.arena_free(llarena.getfakearenaaddress(adr1))
 
-    def clear(self):
+    def free_and_clear(self):
         obj = self.chained_list
         self.chained_list = NULL
         while obj:
diff --git a/pypy/rpython/memory/gc/stmtls.py b/pypy/rpython/memory/gc/stmtls.py
--- a/pypy/rpython/memory/gc/stmtls.py
+++ b/pypy/rpython/memory/gc/stmtls.py
@@ -38,25 +38,21 @@
         self.nursery_size  = self.gc.nursery_size
         self.nursery_start = self._alloc_nursery(self.nursery_size)
         #
-        # --- the local raw-malloced objects (chained list via hdr.version)
-        #self.rawmalloced_objects = NULL
-        # --- the local "normal" old objects (chained list via hdr.version)
-        #self.old_objects = NULL
-        # --- the local objects with weakrefs (chained list via hdr.version)
-        #self.young_objects_with_weakrefs = NULL
-        #self.old_objects_with_weakrefs = NULL
-        #
         # --- a thread-local allocator for the shared area
         from pypy.rpython.memory.gc.stmshared import StmGCThreadLocalAllocator
         self.sharedarea_tls = StmGCThreadLocalAllocator(self.gc.sharedarea)
         # --- the LOCAL objects with GCFLAG_WAS_COPIED
         self.copied_local_objects = self.AddressStack()
+        # --- the LOCAL objects which are weakrefs.  They are also listed
+        #     in the appropriate place, like sharedarea_tls, if needed.
+        self.local_weakrefs = self.AddressStack()
         #
         self._register_with_C_code()
 
     def teardown_thread(self):
         self._cleanup_state()
         self._unregister_with_C_code()
+        self.local_weakrefs.delete()
         self.copied_local_objects.delete()
         self.sharedarea_tls.delete()
         self._free_nursery(self.nursery_start)
@@ -199,9 +195,9 @@
         # Now repeatedly follow objects until 'pending' is empty.
         self.collect_flush_pending()
         #
-        # Walk the list of LOCAL raw-malloced objects, and free them if
-        # necessary.
-        #self.free_local_rawmalloced_objects()
+        # Walk the list of LOCAL weakrefs, and update it if necessary.
+        if self.local_weakrefs.non_empty():
+            self.update_local_weakrefs()
         #
         # Visit all previous OLD objects.  Free the ones that have not been
         # visited above, and reset GCFLAG_VISITED on the others.
@@ -253,6 +249,9 @@
         self.copied_local_objects.append(localobj)
         return localobj
 
+    def fresh_new_weakref(self, obj):
+        self.local_weakrefs.append(obj)
+
     # ------------------------------------------------------------
 
     def _promote_locals_to_globals(self):
@@ -275,12 +274,14 @@
 
         # free the old unused local objects still allocated in the
         # StmGCThreadLocalAllocator
-        self.sharedarea_tls.clear()
+        self.sharedarea_tls.free_and_clear()
         # free the local copies.  Note that commonly, they are leftovers
         # from the previous transaction running in this thread.  The C code
         # has just copied them over the corresponding GLOBAL objects at the
         # very end of that transaction.
         self._free_and_clear_list(self.copied_local_objects)
+        # forget the local weakrefs.
+        self.local_weakrefs.clear()
 
     def _free_and_clear_list(self, lst):
         while lst.non_empty():
@@ -302,6 +303,33 @@
     def _trace_drag_out1(self, root):
         self._trace_drag_out(root, None)
 
+    @always_inline
+    def categorize_object(self, obj, can_be_in_nursery):
+        """Return the current surviving state of the object:
+            0: not marked as surviving, so far
+            1: survives and does not move
+            2: survives, but moves to 'hdr.version'
+        """
+        hdr = self.gc.header(obj)
+        flag_combination = hdr.tid & (GCFLAG_GLOBAL |
+                                      GCFLAG_WAS_COPIED |
+                                      GCFLAG_VISITED)
+        if flag_combination == 0:
+            return 0    # not marked as surviving, so far
+
+        if flag_combination == self.detect_flag_combination:
+            # At a normal time, self.detect_flag_combination is -1
+            # and this case is never seen.  At end of transactions,
+            # detect_flag_combination is GCFLAG_WAS_COPIED|GCFLAG_VISITED.
+            # This case is to force pointers to the LOCAL copy to be
+            # replaced with pointers to the GLOBAL copy.
+            return 2
+
+        if can_be_in_nursery and self.is_in_nursery(obj):
+            return 2
+        else:
+            return 1
+
     def _trace_drag_out(self, root, ignored):
         """Trace callback: 'root' is the address of some pointer.  If that
         pointer points to a YOUNG object, allocate an OLD copy of it and
@@ -317,18 +345,11 @@
         if not self.is_in_nursery(obj):
             # we ignore both GLOBAL objects and objects which have already
             # been VISITED
-            flag_combination = hdr.tid & (GCFLAG_GLOBAL |
-                                          GCFLAG_WAS_COPIED |
-                                          GCFLAG_VISITED)
-            if flag_combination == 0:
+            cat = self.categorize_object(obj, can_be_in_nursery=False)
+            if cat == 0:
                 hdr.tid |= GCFLAG_VISITED
                 self.pending.append(obj)
-            elif flag_combination == self.detect_flag_combination:
-                # At a normal time, self.detect_flag_combination is -1
-                # and this case is never seen.  At end of transactions,
-                # detect_flag_combination is GCFLAG_WAS_COPIED|GCFLAG_VISITED.
-                # Replace references to the local copy with references
-                # to the global copy
+            elif cat == 2:
                 root.address[0] = hdr.version
             return
         #
@@ -437,6 +458,34 @@
             self.trace_and_drag_out_of_nursery(obj)
         self.pending.delete()
 
+    def update_local_weakrefs(self):
+        old = self.local_weakrefs
+        new = self.AddressStack()
+        while old.non_empty():
+            obj = old.pop()
+            hdr = self.gc.header(obj)
+            if hdr.tid & GCFLAG_VISITED == 0:
+                continue # weakref itself dies
+            #
+            if self.is_in_nursery(obj):
+                obj = hdr.version
+                hdr = self.gc.header(obj)
+            offset = self.gc.weakpointer_offset(self.gc.get_type_id(obj))
+            pointing_to = (obj + offset).address[0]
+            cat = self.categorize_object(pointing_to, can_be_in_nursery=True)
+            if cat == 0:
+                # the weakref points to a dying object; no need to rememeber it
+                (obj + offset).address[0] = llmemory.NULL
+            else:
+                # the weakref points to an object that stays alive
+                if cat == 2:
+                    pointing_hdr = self.gc.header(pointing_to)
+                    (obj + offset).address[0] = pointing_hdr.version
+                new.append(obj)    # stays alive
+        #
+        self.local_weakrefs = new
+        old.delete()
+
     def mass_free_old_local(self, previous_sharedarea_tls):
         obj = previous_sharedarea_tls.chained_list
         previous_sharedarea_tls.delete()
diff --git a/pypy/rpython/memory/gc/test/test_stmgc.py b/pypy/rpython/memory/gc/test/test_stmgc.py
--- a/pypy/rpython/memory/gc/test/test_stmgc.py
+++ b/pypy/rpython/memory/gc/test/test_stmgc.py
@@ -582,7 +582,7 @@
         twr1.wr = wr1
         self.gc.commit_transaction()
         wr2 = twr1.wr      # twr1 is a root, so not copied yet
-        assert wr2 and wr2 != wr1
+        assert wr2 and wr2._obj0 != wr1._obj0
         assert wr2.wadr == s2_adr   # survives
 
     def test_weakref_to_local_dying(self):
@@ -596,7 +596,7 @@
         twr1.wr = wr1
         self.gc.commit_transaction()
         wr2 = twr1.wr      # twr1 is a root, so not copied yet
-        assert wr2 and wr2 != wr1
+        assert wr2 and wr2._obj0 != wr1._obj0
         assert wr2.wadr == llmemory.NULL   # dies
 
     def test_weakref_to_local_surviving(self):
@@ -615,8 +615,8 @@
         t2.a = 4242
         self.gc.commit_transaction()
         wr2 = twr1.wr      # twr1 is a root, so not copied yet
-        assert wr2 and wr2 != wr1
-        assert wr2.wadr and wr2.wadr != t2_adr       # survives
+        assert wr2 and wr2._obj0 != wr1._obj0
+        assert wr2.wadr and wr2.wadr.ptr._obj0 != t2_adr.ptr._obj0   # survives
         s2 = llmemory.cast_adr_to_ptr(wr2.wadr, lltype.Ptr(S))
         assert s2.a == 4242
         assert s2 == tr1.s1   # tr1 is a root, so not copied yet


More information about the pypy-commit mailing list