Author: Gregor Wegberg <[email protected]>
Branch: gc-incminimark-pinning
Changeset: r71817:f804676aaa5e
Date: 2014-05-14 14:14 +0200
http://bitbucket.org/pypy/pypy/changeset/f804676aaa5e/

Log:    first version passing `test_simple_pin` in `test_object_pinning.py`.

        does not translate right now.

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
@@ -495,11 +495,6 @@
         self.set_major_threshold_from(0.0)
         ll_assert(self.extra_threshold == 0, "extra_threshold set too early")
         self.initial_cleanup = self.nursery_size
-
-        # XXX remove (groggi)
-        debug_print("nursery start ", self.nursery)
-        debug_print("nursery top ", self.nursery_top)
-
         debug_stop("gc-set-nursery-size")
 
 
@@ -706,37 +701,82 @@
         and finally reserve 'totalsize' bytes at the start of the
         now-empty nursery.
         """
-        if self.nursery_top < self.nursery_real_top:
-            self.move_nursery_top(totalsize)
-            return prev_result
-        self.minor_collection()
-        #
-        # If the gc_state is not STATE_SCANNING, we're in the middle of
-        # an incremental major collection.  In this case, always progress
-        # one step.  If the gc_state is STATE_SCANNING, wait until there
-        # is too much garbage before starting the next major collection.
-        if (self.gc_state != STATE_SCANNING or
-                    self.get_total_memory_used() >
-                    self.next_major_collection_threshold):
-            self.major_collection_step()
+        # XXX update doc to contain nursery_barrier (groggi)
+
+        # keep track how many iteration we've gone trough
+        count = 0
+        while True:
+            if self.nursery_barriers.non_empty():
+                # we have multiple blocks of free memory in the nursery
+                # which are divided by pinned object. First thing to do
+                # is to move to the next free block.
+                size_gc_header = self.gcheaderbuilder.size_gc_header
+                pinned_obj_size = size_gc_header + self.get_size(
+                    self.nursery_top + size_gc_header)
+                    # ^^^ nursery_top points to the beginning of the header of
+                    # the next object. To get the right address to call
+                    # get_size(), we need to add the header to the address.
+                #
+                # move search area to the next free memory block in the
+                # nursery.
+                self.nursery_free = self.nursery_top + pinned_obj_size
+                self.move_nursery_top(llarena.getfakearenaaddress(
+                    self.nursery_barriers.popleft()) - self.nursery_free)
+            else:
+                count += 1
+                #
+                # no barriers (i.e. pinned objects) left. Check if there is
+                # enough space till we reach the real top of the nursery.
+                if self.nursery_top < self.nursery_real_top:
+                    self.move_nursery_top(totalsize)
+                    return prev_result
+                #
+                self.minor_collection()
+                if count == 1:
+                    #
+                    # If the gc_state is not STATE_SCANNING, we're in the 
middle of
+                    # an incremental major collection.  In this case, always 
progress
+                    # one step.  If the gc_state is STATE_SCANNING, wait until 
there
+                    # is too much garbage before starting the next major 
collection.
+                    if (self.gc_state != STATE_SCANNING or
+                                self.get_total_memory_used() >
+                                self.next_major_collection_threshold):
+                        self.major_collection_step()
+                        #
+                        # The nursery might not be empty now, because of
+                        # execute_finalizers().  If it is almost full again,
+                        # we need to fix it with another call to 
minor_collection().
+                        if self.nursery_free + totalsize > self.nursery_top:
+                            #
+                            if self.nursery_free + totalsize > 
self.nursery_real_top:
+                                self.minor_collection()
+                                # then the nursery is empty
+                                # XXX ^^^ not necessarily, update comment 
(groggi)
+                            else:
+                                # we just need to clean up a bit more of the 
nursery
+                                #self.move_nursery_top(totalsize)
+                                # do a loop, should take care of finding space
+                                # XXX ^^^ rewrite comment the moment we're 
sure it's
+                                # the correct way.
+                                pass
+                else:
+                    ll_assert(count == 2,
+                        "Seeing minor_collection() at least twice. "
+                        "Too many pinned objects?")
+
             #
-            # The nursery might not be empty now, because of
-            # execute_finalizers().  If it is almost full again,
-            # we need to fix it with another call to minor_collection().
-            if self.nursery_free + totalsize > self.nursery_top:
-                #
-                if self.nursery_free + totalsize > self.nursery_real_top:
-                    self.minor_collection()
-                    # then the nursery is empty
-                else:
-                    # we just need to clean up a bit more of the nursery
-                    self.move_nursery_top(totalsize)
-        #
-        result = self.nursery_free
-        self.nursery_free = result + totalsize
-        ll_assert(self.nursery_free <= self.nursery_top, "nursery overflow")
+            # attempt to get 'totalzise' out of the nursery now.  This may
+            # fail again, and then we loop. Should be the uncommon case.
+            # XXX measure "uncommon" case (groggi)
+            result = self.nursery_free
+            self.nursery_free = result + totalsize
+            if self.nursery_free <= self.nursery_top:
+                break
         #
         if self.debug_tiny_nursery >= 0:   # for debugging
+            # XXX solution for this assert? (groggi)
+            ll_assert(not self.nursery_barriers.non_empty(),
+                "no support for nursery debug and pinning")
             if self.nursery_top - self.nursery_free > self.debug_tiny_nursery:
                 self.nursery_free = self.nursery_top - self.debug_tiny_nursery
         #
@@ -911,7 +951,6 @@
         return self.is_in_nursery(obj)
 
     def pin(self, obj):
-        debug_start("groggi-incminimark-pin")
         # Tries to pin the given 'obj'.  On success this method returns True,
         # otherwise False. There are multiple reasons why a call returns False
         # and it should be always expected that pinning is likely to fail
@@ -934,21 +973,16 @@
 
         self.header(obj).tid |= GCFLAG_PINNED
         self.pinned_objects_in_nursery += 1
-        debug_print("pinned_objects_in_nursery: ", 
self.pinned_objects_in_nursery)
-        debug_stop("groggi-incminimark-pin")
         return True
 
 
     def unpin(self, obj):
         # Unpins a previously pinned 'obj'.  This should only be called
         # after a pin(obj).
-        debug_start("groggi-incminimark-unpin")
         ll_assert(self.header(obj) & GCFLAG_PINNED != 0,
             "unpin: object is already not pinned")
         self.header(obj).tid &= ~GCFLAG_PINNED
         self.pinned_objects_in_nursery -= 1
-        debug_print("pinned_objects_in_nursery: ", 
self.pinned_objects_in_nursery)
-        debug_stop("groggi-incminimark-unpin")
 
 
     def shrink_array(self, obj, smallerlength):
@@ -1119,11 +1153,19 @@
 
     def debug_check_object(self, obj):
         # We are after a minor collection, and possibly after a major
-        # collection step.  No object should be in the nursery
-        ll_assert(not self.is_in_nursery(obj),
-                  "object in nursery after collection")
-        ll_assert(self.header(obj).tid & GCFLAG_VISITED_RMY == 0,
-                  "GCFLAG_VISITED_RMY after collection")
+        # collection step.  No object should be in the nursery (except
+        # pinned ones)
+        if self.header(obj).tid & GCFLAG_PINNED == 0:
+            ll_assert(not self.is_in_nursery(obj),
+                      "object in nursery after collection")
+            ll_assert(self.header(obj).tid & GCFLAG_VISITED_RMY == 0,
+                      "GCFLAG_VISITED_RMY after collection")
+        else:
+            # pinned objects are always in the nursery
+            ll_assert(self.is_in_nursery(obj),
+                      "pinned object not in nursery")
+            # XXX gc-minimark-pinning checks for GCFLAG_TRACK_YOUNG_POINTER
+            # (groggi)
 
         if self.gc_state == STATE_SCANNING:
             self._debug_check_object_scanning(obj)
@@ -1169,9 +1211,11 @@
         # All objects should have this flag, except if they
         # don't have any GC pointer
         typeid = self.get_type_id(obj)
-        if self.has_gcptr(typeid):
-            ll_assert(self.header(obj).tid & GCFLAG_TRACK_YOUNG_PTRS != 0,
-                      "missing GCFLAG_TRACK_YOUNG_PTRS")
+        if not self.header(obj).tid & GCFLAG_PINNED:
+            # XXX do we need checks if the object is actually pinned? (groggi)
+            if self.has_gcptr(typeid):
+                ll_assert(self.header(obj).tid & GCFLAG_TRACK_YOUNG_PTRS != 0,
+                          "missing GCFLAG_TRACK_YOUNG_PTRS")
         # the GCFLAG_FINALIZATION_ORDERING should not be set between coll.
         ll_assert(self.header(obj).tid & GCFLAG_FINALIZATION_ORDERING == 0,
                   "unexpected GCFLAG_FINALIZATION_ORDERING")
@@ -1466,6 +1510,18 @@
             self.old_objects_pointing_to_young.foreach(
                 self._add_to_more_objects_to_trace, None)
         #
+        # Keeps track of surviving pinned objects. See also `_trace_drag_out()`
+        # where this stack is filled.
+        self.surviving_pinned_objects = self.AddressStack()
+        #
+        # The following counter keeps track of the amount of alive and pinned
+        # objects inside the nursery.  The counter is reset, as we have to
+        # check which pinned objects are actually still alive. Pinning an
+        # object does not prevent the removal of an object, if it's not used
+        # anymore.
+        # XXX is this true? does it make sense? (groggi)
+        self.pinned_objects_in_nursery = 0
+        #
         # First, find the roots that point to young objects.  All nursery
         # objects found are copied out of the nursery, and the occasional
         # young raw-malloced object is flagged with GCFLAG_VISITED_RMY.
@@ -1505,6 +1561,8 @@
             self.deal_with_young_objects_with_finalizers()
         #
         # Clear this mapping.
+        # XXX gc-minimark-pinning contains some additional code
+        # in regard to pinned object. TODO (groggi)
         if self.nursery_objects_shadows.length() > 0:
             self.nursery_objects_shadows.clear()
         #
@@ -1513,17 +1571,64 @@
         if self.young_rawmalloced_objects:
             self.free_young_rawmalloced_objects()
         #
-        # All live nursery objects are out, and the rest dies.  Fill
-        # the nursery up to the cleanup point with zeros
-        llarena.arena_reset(self.nursery, self.nursery_size, 0)
-        llarena.arena_reset(self.nursery, self.initial_cleanup, 2)
-        self.debug_rotate_nursery()
+        # All live nursery objects are out of the nursery or pinned inside
+        # the nursery.  Create nursery barriers to protect the pinned object,
+        # fill the rest of the nursery with zeros and reset the current nursery
+        # pointer.
+        size_gc_header = self.gcheaderbuilder.size_gc_header
+        nursery_barriers = self.AddressDeque()
+        prev = self.nursery
+        self.surviving_pinned_objects.sort()
+        assert self.pinned_objects_in_nursery == \
+            self.surviving_pinned_objects.length()
+        while self.surviving_pinned_objects.non_empty():
+            #
+            # prepare information about the surviving pinned object
+            next = self.surviving_pinned_objects.pop()
+            assert next >= prev
+            #
+            # clear the arena between the last pinned object or arena start
+            # and the pinned object
+            pinned_obj_size = llarena.getfakearenaaddress(next) - prev
+            llarena.arena_reset(prev, pinned_obj_size, 2)
+            #
+            # clean up object's flags
+            obj = next + size_gc_header
+            self.header(obj).tid &= ~GCFLAG_VISITED
+            #
+            # create a new nursery barrier for the pinned object
+            nursery_barriers.append(next)
+            #
+            # update 'prev' to the end of the 'next' object
+            prev = prev + pinned_obj_size + \
+                (size_gc_header + self.get_size(obj))
+        #
+        # clear the rest of the arena
+        llarena.arena_reset(prev, self.nursery_real_top - prev, 2)
+        #                         ^^^ calculate the size of the last continuous
+        #                             arena block.
+        #
+        self.surviving_pinned_objects.delete()
+        self.nursery_barriers.delete()
+        self.nursery_barriers = nursery_barriers
+        # XXX gc-minimark-pinning does a debug_rotate_nursery() here (groggi)
         self.nursery_free = self.nursery
-        self.nursery_top = self.nursery + self.initial_cleanup
-        self.nursery_real_top = self.nursery + self.nursery_size
+        self.nursery_barriers.append(self.nursery + self.nursery_size)
+        self.nursery_top = self.nursery_barriers.popleft()
+
+# All live nursery objects are out, and the rest dies.  Fill
+# the nursery up to the cleanup point with zeros
+#        llarena.arena_reset(self.nursery, self.nursery_size, 0)
+#        llarena.arena_reset(self.nursery, self.initial_cleanup, 2)
+#        self.debug_rotate_nursery()
+#        self.nursery_free = self.nursery
+#        self.nursery_top = self.nursery + self.initial_cleanup
+#        self.nursery_real_top = self.nursery + self.nursery_size
         #
         debug_print("minor collect, total memory used:",
                     self.get_total_memory_used())
+        debug_print("number of pinned objects:",
+                    self.pinned_objects_in_nursery)
         if self.DEBUG >= 2:
             self.debug_check_consistency()     # expensive!
         #
@@ -1685,7 +1790,7 @@
             return
         #
         size_gc_header = self.gcheaderbuilder.size_gc_header
-        if self.header(obj).tid & GCFLAG_HAS_SHADOW == 0:
+        if self.header(obj).tid & (GCFLAG_HAS_SHADOW | GCFLAG_PINNED) == 0:
             #
             # Common case: 'obj' was not already forwarded (otherwise
             # tid == -42, containing all flags), and it doesn't have the
@@ -1696,12 +1801,25 @@
             newhdr = self._malloc_out_of_nursery(totalsize)
             #
         elif self.is_forwarded(obj):
+            # XXX check if GCFLAG_PINNED is relevant for this case (groggi)
             #
             # 'obj' was already forwarded.  Change the original reference
             # to point to its forwarding address, and we're done.
             root.address[0] = self.get_forwarding_address(obj)
             return
             #
+        elif self.header(obj).tid & GCFLAG_PINNED:
+            hdr = self.header(obj)
+            if hdr.tid & GCFLAG_VISITED:
+                # already visited and keeping track of the object
+                return
+            hdr.tid |= GCFLAG_VISITED
+            # XXX check for object flags that are not supported alongside
+            # GCFLAG_PINNED (groggi)
+            self.surviving_pinned_objects.append(
+                llarena.getfakearenaaddress(obj - size_gc_header))
+            self.pinned_objects_in_nursery += 1
+            return
         else:
             # First visit to an object that has already a shadow.
             newobj = self.nursery_objects_shadows.get(obj)
@@ -1857,7 +1975,7 @@
         # Debugging checks
         ll_assert(self.nursery_free == self.nursery,
                   "nursery not empty in major_collection_step()")
-        self.debug_check_consistency()
+        self.debug_check_consistency() # YYY
 
 
         # XXX currently very course increments, get this working then split
@@ -2108,7 +2226,8 @@
         # and the GCFLAG_VISITED will be reset at the end of the
         # collection.
         hdr = self.header(obj)
-        if hdr.tid & (GCFLAG_VISITED | GCFLAG_NO_HEAP_PTRS):
+        if hdr.tid & (GCFLAG_VISITED | GCFLAG_NO_HEAP_PTRS | GCFLAG_PINNED):
+            # XXX ^^^ update doc in any way because of GCFLAG_PINNED addition? 
(groggi)
             return 0
         #
         # It's the first time.  We set the flag VISITED.  The trick is
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to