[pypy-commit] pypy gc-forkfriendly-2: Tweak an existing gc flag so that it can be used as an indicator for

arigo pypy.commits at gmail.com
Thu Feb 23 08:46:21 EST 2017


Author: Armin Rigo <arigo at tunes.org>
Branch: gc-forkfriendly-2
Changeset: r90323:6aee6fc9c814
Date: 2017-02-23 14:42 +0100
http://bitbucket.org/pypy/pypy/changeset/6aee6fc9c814/

Log:	Tweak an existing gc flag so that it can be used as an indicator for
	whether an old object is inside a minimarkpage-allocated arena or
	not

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
@@ -71,7 +71,7 @@
 from rpython.rlib.rarithmetic import ovfcheck, LONG_BIT, intmask, r_uint
 from rpython.rlib.rarithmetic import LONG_BIT_SHIFT
 from rpython.rlib.debug import ll_assert, debug_print, debug_start, debug_stop
-from rpython.rlib.objectmodel import specialize
+from rpython.rlib.objectmodel import specialize, we_are_translated
 from rpython.memory.gc.minimarkpage import out_of_memory
 
 #
@@ -135,9 +135,13 @@
 # note that GCFLAG_CARDS_SET is the most significant bit of a byte:
 # this is required for the JIT (x86)
 
-# The following flag is set on surviving raw-malloced young objects during
-# a minor collection.
-GCFLAG_VISITED_RMY   = first_gcflag << 8
+# The following flag is set on all *old* objects that are not allocated
+# with the normal minimarkpage mechanism.  During minor collections, it
+# is used to track surviving raw-malloced young objects (they don't have
+# the flag to start with, but it gets added if they survive).  Once the
+# minor collection is complete, all objects have the flag iff they are
+# not located inside the minimarkpage.
+GCFLAG_OLD_OUTSIDE_MINIMARKPAGE = first_gcflag << 8
 
 # The following flag is set on nursery objects to keep them in the nursery.
 # This means that a young object with this flag is not moved out
@@ -1002,7 +1006,8 @@
                 self.young_rawmalloced_objects.add(result + size_gc_header)
             else:
                 self.old_rawmalloced_objects.append(result + size_gc_header)
-                extra_flags |= GCFLAG_TRACK_YOUNG_PTRS
+                extra_flags |= (GCFLAG_TRACK_YOUNG_PTRS |
+                                GCFLAG_OLD_OUTSIDE_MINIMARKPAGE)
         #
         # Common code to fill the header and length of the object.
         self.init_gc_object(result, typeid, extra_flags)
@@ -1126,7 +1131,8 @@
     def init_gc_object_immortal(self, addr, typeid16, flags=0):
         # For prebuilt GC objects, the flags must contain
         # GCFLAG_NO_xxx_PTRS, at least initially.
-        flags |= GCFLAG_NO_HEAP_PTRS | GCFLAG_TRACK_YOUNG_PTRS
+        flags |= (GCFLAG_NO_HEAP_PTRS | GCFLAG_TRACK_YOUNG_PTRS
+                  | GCFLAG_OLD_OUTSIDE_MINIMARKPAGE)
         self.init_gc_object(addr, typeid16, flags)
 
     def is_in_nursery(self, addr):
@@ -1241,13 +1247,21 @@
         if not self._is_pinned(obj):
             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")
+            if not we_are_translated():
+                size_gc_header = self.gcheaderbuilder.size_gc_header
+                if self.ac._is_inside_minimarkpage(obj - size_gc_header):
+                    expected_flag = 0
+                else:
+                    expected_flag = GCFLAG_OLD_OUTSIDE_MINIMARKPAGE
+                assert ((self.header(obj).tid & GCFLAG_OLD_OUTSIDE_MINIMARKPAGE)
+                        == expected_flag)
             ll_assert(self.header(obj).tid & GCFLAG_PINNED == 0,
                       "GCFLAG_PINNED outside the nursery after collection")
         else:
             ll_assert(self.is_in_nursery(obj),
                       "pinned object not in nursery")
+            ll_assert((self.header(obj).tid & GCFLAG_OLD_OUTSIDE_MINIMARKPAGE)
+                      != 0, "pinned object must be OLD_OUTSIDE_MINIMARKPAGE")
 
         if self.gc_state == STATE_SCANNING:
             self._debug_check_object_scanning(obj)
@@ -1660,7 +1674,8 @@
         #
         # 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.
+        # young raw-malloced object is flagged with
+        # GCFLAG_OLD_OUTSIDE_MINIMARKPAGE.
         # Note that during this step, we ignore references to further
         # young objects; only objects directly referenced by roots
         # are copied out or flagged.  They are also added to the list
@@ -1702,7 +1717,7 @@
             # All nursery objects they reference are copied out of the
             # nursery, and again added to 'old_objects_pointing_to_young'.
             # All young raw-malloced object found are flagged
-            # GCFLAG_VISITED_RMY.
+            # GCFLAG_OLD_OUTSIDE_MINIMARKPAGE.
             # We proceed until 'old_objects_pointing_to_young' is empty.
             self.collect_oldrefs_to_nursery()
             #
@@ -1979,7 +1994,8 @@
         #print '_trace_drag_out(%x: %r)' % (hash(obj.ptr._obj), obj)
         #
         # If 'obj' is not in the nursery, nothing to change -- expect
-        # that we must set GCFLAG_VISITED_RMY on young raw-malloced objects.
+        # that we must set GCFLAG_OLD_OUTSIDE_MINIMARKPAGE on young
+        # raw-malloced objects.
         if not self.is_in_nursery(obj):
             # cache usage trade-off: I think that it is a better idea to
             # check if 'obj' is in young_rawmalloced_objects with an access
@@ -1993,6 +2009,7 @@
         # copy the contents of the object? usually yes, but not for some
         # shadow objects
         copy = True
+        extra_flag = 0
         #
         size_gc_header = self.gcheaderbuilder.size_gc_header
         if self.header(obj).tid & (GCFLAG_HAS_SHADOW | GCFLAG_PINNED) == 0:
@@ -2003,7 +2020,7 @@
             # into a new nonmovable location.
             totalsize = size_gc_header + self.get_size(obj)
             self.nursery_surviving_size += raw_malloc_usage(totalsize)
-            newhdr = self._malloc_out_of_nursery(totalsize)
+            newhdr, extra_flag = self._malloc_out_of_nursery(totalsize)
             #
         elif self.is_forwarded(obj):
             #
@@ -2032,7 +2049,7 @@
             if hdr.tid & GCFLAG_VISITED:
                 return
             #
-            hdr.tid |= GCFLAG_VISITED
+            hdr.tid |= GCFLAG_VISITED | GCFLAG_OLD_OUTSIDE_MINIMARKPAGE
             #
             self.surviving_pinned_objects.append(
                 llarena.getfakearenaaddress(obj - size_gc_header))
@@ -2055,11 +2072,15 @@
             #
             totalsize = size_gc_header + self.get_size(obj)
             self.nursery_surviving_size += raw_malloc_usage(totalsize)
+            extra_flag = (
+                self.header(newobj).tid & GCFLAG_OLD_OUTSIDE_MINIMARKPAGE)
         #
         # Copy it.  Note that references to other objects in the
         # nursery are kept unchanged in this step.
         if copy:
+            tid = self.header(obj).tid &~ GCFLAG_OLD_OUTSIDE_MINIMARKPAGE
             llmemory.raw_memcopy(obj - size_gc_header, newhdr, totalsize)
+            self.header(newhdr + size_gc_header).tid = tid | extra_flag
         #
         # Set the old object's tid to -42 (containing all flags) and
         # replace the old object's content with the target address.
@@ -2090,15 +2111,16 @@
     def _visit_young_rawmalloced_object(self, obj):
         # 'obj' points to a young, raw-malloced object.
         # Any young rawmalloced object never seen by the code here
-        # will end up without GCFLAG_VISITED_RMY, and be freed at the
+        # will end up without GCFLAG_OLD_OUTSIDE_MINIMARKPAGE,
+        # and be freed at the
         # end of the current minor collection.  Note that there was
         # a bug in which dying young arrays with card marks would
         # still be scanned before being freed, keeping a lot of
         # objects unnecessarily alive.
         hdr = self.header(obj)
-        if hdr.tid & GCFLAG_VISITED_RMY:
+        if hdr.tid & GCFLAG_OLD_OUTSIDE_MINIMARKPAGE:
             return
-        hdr.tid |= GCFLAG_VISITED_RMY
+        hdr.tid |= GCFLAG_OLD_OUTSIDE_MINIMARKPAGE
         #
         # Accounting
         size_gc_header = self.gcheaderbuilder.size_gc_header
@@ -2126,10 +2148,11 @@
         'totalsize' that lives so far in the nursery."""
         if raw_malloc_usage(totalsize) <= self.small_request_threshold:
             # most common path
-            return self.ac.malloc(totalsize)
+            return (self.ac.malloc(totalsize), 0)
         else:
             # for nursery objects that are not small
-            return self._malloc_out_of_nursery_nonsmall(totalsize)
+            return (self._malloc_out_of_nursery_nonsmall(totalsize),
+                    GCFLAG_OLD_OUTSIDE_MINIMARKPAGE)
     _malloc_out_of_nursery._always_inline_ = True
 
     def _malloc_out_of_nursery_nonsmall(self, totalsize):
@@ -2154,9 +2177,10 @@
         self.young_rawmalloced_objects = self.null_address_dict()
 
     def _free_young_rawmalloced_obj(self, obj, ignored1, ignored2):
-        # If 'obj' has GCFLAG_VISITED_RMY, it was seen by _trace_drag_out
-        # and survives.  Otherwise, it dies.
-        self.free_rawmalloced_object_if_unvisited(obj, GCFLAG_VISITED_RMY)
+        # If 'obj' has GCFLAG_OLD_OUTSIDE_MINIMARKPAGE, it was seen by
+        # _trace_drag_out and survives.  Otherwise, it dies.
+        self.free_rawmalloced_object_if_unvisited(obj,
+            GCFLAG_OLD_OUTSIDE_MINIMARKPAGE, remove_flag=False)
 
     def remove_young_arrays_from_old_objects_pointing_to_young(self):
         old = self.old_objects_pointing_to_young
@@ -2434,9 +2458,11 @@
     def _reset_gcflag_visited(self, obj, ignored):
         self.header(obj).tid &= ~GCFLAG_VISITED
 
-    def free_rawmalloced_object_if_unvisited(self, obj, check_flag):
+    def free_rawmalloced_object_if_unvisited(self, obj, check_flag,
+                                             remove_flag=True):
         if self.header(obj).tid & check_flag:
-            self.header(obj).tid &= ~check_flag   # survives
+            if remove_flag:
+                self.header(obj).tid &= ~check_flag   # survives
             self.old_rawmalloced_objects.append(obj)
         else:
             size_gc_header = self.gcheaderbuilder.size_gc_header
@@ -2590,8 +2616,9 @@
     def _allocate_shadow(self, obj, copy=False):
         size_gc_header = self.gcheaderbuilder.size_gc_header
         size = self.get_size(obj)
-        shadowhdr = self._malloc_out_of_nursery(size_gc_header +
-                                                size)
+        shadowhdr, shadowflag = self._malloc_out_of_nursery(size_gc_header +
+                                                            size)
+
         # Initialize the shadow enough to be considered a
         # valid gc object.  If the original object stays
         # alive at the next minor collection, it will anyway
@@ -2615,6 +2642,9 @@
             totalsize = size_gc_header + self.get_size(obj)
             llmemory.raw_memcopy(obj - size_gc_header, shadow, totalsize)
 
+        if shadowflag != 0:
+            self.header(shadow).tid |= shadowflag
+
         return shadow
 
     def _find_shadow(self, obj):
@@ -2847,7 +2877,8 @@
             elif (bool(self.young_rawmalloced_objects) and
                   self.young_rawmalloced_objects.contains(pointing_to)):
                 # young weakref to a young raw-malloced object
-                if self.header(pointing_to).tid & GCFLAG_VISITED_RMY:
+                if (self.header(pointing_to).tid &
+                        GCFLAG_OLD_OUTSIDE_MINIMARKPAGE):
                     pass    # survives, but does not move
                 else:
                     (obj + offset).address[0] = llmemory.NULL
@@ -3035,7 +3066,7 @@
         elif (bool(self.young_rawmalloced_objects) and
               self.young_rawmalloced_objects.contains(obj)):
             # young weakref to a young raw-malloced object
-            if self.header(obj).tid & GCFLAG_VISITED_RMY:
+            if self.header(obj).tid & GCFLAG_OLD_OUTSIDE_MINIMARKPAGE:
                 surviving = True    # survives, but does not move
             else:
                 surviving = False
diff --git a/rpython/memory/gc/minimarkpage.py b/rpython/memory/gc/minimarkpage.py
--- a/rpython/memory/gc/minimarkpage.py
+++ b/rpython/memory/gc/minimarkpage.py
@@ -1,7 +1,7 @@
 import sys
 from rpython.rtyper.lltypesystem import lltype, llmemory, llarena, rffi
 from rpython.rlib.rarithmetic import LONG_BIT, r_uint
-from rpython.rlib.objectmodel import we_are_translated
+from rpython.rlib.objectmodel import we_are_translated, not_rpython
 from rpython.rlib.debug import ll_assert, fatalerror
 
 WORD = LONG_BIT // 8
@@ -88,6 +88,7 @@
 class ArenaCollection(object):
     _alloc_flavor_ = "raw"
 
+    @not_rpython
     def __init__(self, arena_size, page_size, small_request_threshold):
         # 'small_request_threshold' is the largest size that we
         # can ask with self.malloc().
@@ -295,6 +296,8 @@
         arena_base = llarena.arena_malloc(self.arena_size, False)
         if not arena_base:
             out_of_memory("out of memory: couldn't allocate the next arena")
+        if not we_are_translated():
+            arena_base.arena._from_minimarkpage = True
         arena_end = arena_base + self.arena_size
         #
         # 'firstpage' points to the first unused page
@@ -570,6 +573,17 @@
         return nblocks - num_initialized_blocks
 
 
+    @not_rpython
+    def _is_inside_minimarkpage(self, hdr):
+        # check that getfakearenaaddress() works and the
+        # arena is flagged as "from minimarkpage"
+        try:
+            arena = llarena.getfakearenaaddress(hdr).arena
+        except RuntimeError:
+            return False
+        return getattr(arena, '_from_minimarkpage', False)
+
+
 # ____________________________________________________________
 # Helpers to go from a pointer to the start of its page
 
diff --git a/rpython/memory/gc/minimarktest.py b/rpython/memory/gc/minimarktest.py
--- a/rpython/memory/gc/minimarktest.py
+++ b/rpython/memory/gc/minimarktest.py
@@ -28,6 +28,7 @@
         ll_assert((nsize & (WORD-1)) == 0, "malloc: size is not aligned")
         #
         result = llarena.arena_malloc(nsize, False)
+        result.arena._from_minimarktest = True
         llarena.arena_reserve(result, size)
         self.all_objects.append((result, nsize))
         self.total_memory_used += nsize
@@ -56,3 +57,10 @@
         self.mass_free_prepare()
         res = self.mass_free_incremental(ok_to_free_func, sys.maxint)
         assert res
+
+    def _is_inside_minimarkpage(self, hdr):
+        try:
+            arena = llarena.getfakearenaaddress(hdr).arena
+        except RuntimeError:
+            return False
+        return getattr(arena, '_from_minimarktest', False)


More information about the pypy-commit mailing list