[pypy-commit] pypy gc-incminimark-pinning: (wip) add handling for pinned objects referenced by old ones.

groggi noreply at buildbot.pypy.org
Sat Jul 26 16:48:00 CEST 2014


Author: Gregor Wegberg <code at gregorwegberg.com>
Branch: gc-incminimark-pinning
Changeset: r72533:cb701ab5f828
Date: 2014-07-26 16:46 +0200
http://bitbucket.org/pypy/pypy/changeset/cb701ab5f828/

Log:	(wip) add handling for pinned objects referenced by old ones.

	The added AddressStack keeps track of old objects which point to
	young pinned objects. This way if a pinned object is only referenced
	from an old object, it isn't collected as before.

	This is a work in progress. For now test_object_pinning.py tests
	pass. However, there is still a lot to be tested and double checked
	(see XXX). In addition some refactoring & additional comments are in
	order after we make sure it works as expected.

	For now this changes stop pypy from being translated.

diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py
--- a/rpython/memory/gc/incminimark.py
+++ b/rpython/memory/gc/incminimark.py
@@ -381,6 +381,14 @@
         # the nursery.
         self.pinned_objects_in_nursery = 0
         #
+        # Keeps track of objects pointing to pinned objects. These objects
+        # must be revisited every minor collection. Without this list
+        # any old object inside this list would only be visited in case a
+        # write barrier was triggered, which would result in not visiting
+        # the young pinned object and would therefore result in removing
+        # the pinned object.
+        self.old_objects_pointing_to_pinned = self.AddressStack()
+        #
         # Allocate a nursery.  In case of auto_nursery_size, start by
         # allocating a very small nursery, enough to do things like look
         # up the env var, which requires the GC; and then really
@@ -1502,6 +1510,10 @@
         obj = obj + self.gcheaderbuilder.size_gc_header
         shadow = self.nursery_objects_shadows.get(obj)
         if shadow != NULL:
+            # visit shadow to keep it alive
+            # XXX seems like it is save to set GCFLAG_VISITED, however
+            # should be double checked
+            self.header(shadow).tid |= GCFLAG_VISITED
             new_shadow_object_dict.setitem(obj, shadow)
 
     # ----------
@@ -1554,6 +1566,13 @@
         self.nursery_surviving_size = 0
         self.collect_roots_in_nursery()
         #
+        # visit all objects that are known for pointing to pinned
+        # objects. This way we populate 'surviving_pinned_objects'
+        # with pinned object that are (only) visible from an old
+        # object.
+        self.old_objects_pointing_to_pinned.foreach(
+                self._visit_old_objects_pointing_to_pinned, None)
+        #
         while True:
             # If we are using card marking, do a partial trace of the arrays
             # that are flagged with GCFLAG_CARDS_SET.
@@ -1665,6 +1684,8 @@
         #
         debug_stop("gc-minor")
 
+    def _visit_old_objects_pointing_to_pinned(self, obj, ignore):
+        self.trace(obj, self._trace_drag_out, None)
 
     def collect_roots_in_nursery(self):
         # we don't need to trace prebuilt GcStructs during a minor collect:
@@ -1772,7 +1793,7 @@
         """obj must not be in the nursery.  This copies all the
         young objects it references out of the nursery.
         """
-        self.trace(obj, self._trace_drag_out, None)
+        self.trace(obj, self._trace_drag_out, obj)
 
     def trace_and_drag_out_of_nursery_partial(self, obj, start, stop):
         """Like trace_and_drag_out_of_nursery(), but limited to the array
@@ -1801,7 +1822,7 @@
         if not self.header(obj).tid & GCFLAG_VISITED:
             self.more_objects_to_trace.append(obj)
 
-    def _trace_drag_out(self, root, ignored):
+    def _trace_drag_out(self, root, parent):
         obj = root.address[0]
         #print '_trace_drag_out(%x: %r)' % (hash(obj.ptr._obj), obj)
         #
@@ -1842,8 +1863,11 @@
                 # already visited and keeping track of the object
                 return
             hdr.tid |= GCFLAG_VISITED
+            #
+            if parent:
+                self.old_objects_pointing_to_pinned.append(parent)
+            #
             # XXX add additional checks for unsupported pinned objects (groggi)
-            # XXX implement unsupported object types with pinning
             ll_assert(not self.header(obj).tid & GCFLAG_HAS_CARDS,
                 "pinned object with GCFLAG_HAS_CARDS not supported")
             self.surviving_pinned_objects.append(
@@ -2073,6 +2097,15 @@
             #END MARKING
         elif self.gc_state == STATE_SWEEPING:
             #
+            # get rid of objects pointing to pinned objects that were not
+            # visited
+            new_old_objects_pointing_to_pinned = self.AddressStack()
+            self.old_objects_pointing_to_pinned.foreach(
+                    self._sweep_old_objects_pointing_to_pinned,
+                    new_old_objects_pointing_to_pinned)
+            self.old_objects_pointing_to_pinned.delete()
+            self.old_objects_pointing_to_pinned = new_old_objects_pointing_to_pinned
+            #
             if self.raw_malloc_might_sweep.non_empty():
                 # Walk all rawmalloced objects and free the ones that don't
                 # have the GCFLAG_VISITED flag.  Visit at most 'limit' objects.
@@ -2147,6 +2180,10 @@
         debug_print("stopping, now in gc state: ", GC_STATES[self.gc_state])
         debug_stop("gc-collect-step")
 
+    def _sweep_old_objects_pointing_to_pinned(self, obj, new_list):
+        if self.header(obj).tid & GCFLAG_VISITED:
+            new_list.append(obj)
+
     def _free_if_unvisited(self, hdr):
         size_gc_header = self.gcheaderbuilder.size_gc_header
         obj = hdr + size_gc_header


More information about the pypy-commit mailing list