[pypy-commit] pypy gc-forkfriendly: Actually store flags separately from the GC header, when leaving the nursery.

devin.jeanpierre pypy.commits at gmail.com
Wed May 18 00:19:20 EDT 2016


Author: Devin Jeanpierre <jeanpierreda at gmail.com>
Branch: gc-forkfriendly
Changeset: r84511:362654e05ac5
Date: 2016-05-17 21:18 -0700
http://bitbucket.org/pypy/pypy/changeset/362654e05ac5/

Log:	Actually store flags separately from the GC header, when leaving the
	nursery.

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
@@ -155,7 +155,11 @@
 # 'old_objects_pointing_to_pinned' and doesn't have to be added again.
 GCFLAG_PINNED_OBJECT_PARENT_KNOWN = GCFLAG_PINNED
 
-_GCFLAG_FIRST_UNUSED = first_gcflag << 10    # the first unused bit
+# The object is dead or will be dead soon. (Only useful with separate headers
+# that might outlive the object, see incminimark_remoteheader.py)
+GCFLAG_DEAD = first_gcflag << 10
+
+_GCFLAG_FIRST_UNUSED = first_gcflag << 11    # the first unused bit
 
 
 # States for the incremental GC
@@ -2243,8 +2247,7 @@
                 if self.old_objects_with_light_finalizers.non_empty():
                     self.deal_with_old_objects_with_finalizers()
                 # objects_to_trace processed fully, can move on to sweeping
-                self.ac.mass_free_prepare()
-                self.start_free_rawmalloc_objects()
+                self.start_free()
                 #
                 # get rid of objects pointing to pinned objects that were not
                 # visited
@@ -2280,8 +2283,7 @@
                 # GCFLAG_VISITED on the others.  Visit at most '3 *
                 # nursery_size' bytes.
                 limit = 3 * self.nursery_size // self.ac.page_size
-                done = self.ac.mass_free_incremental(self._free_if_unvisited,
-                                                     limit)
+                done = self.free_unvisited_arena_objects_step(limit)
             # XXX tweak the limits above
             #
             if done:
@@ -2348,11 +2350,16 @@
         if self.get_flags(obj) & GCFLAG_VISITED:
             self.remove_flags(obj, GCFLAG_VISITED)
             return False     # survives
-        return True      # dies
+        # dies
+        self.finalize_header(hdr)
+        return True
 
     def _reset_gcflag_visited(self, obj, ignored):
         self.remove_flags(obj, GCFLAG_VISITED)
 
+    def free_unvisited_arena_objects_step(self, limit):
+        return self.ac.mass_free_incremental(self._free_if_unvisited, limit)
+
     def free_rawmalloced_object_if_unvisited(self, obj, check_flag):
         if self.get_flags(obj) & check_flag:
             self.remove_flags(obj, check_flag)   # survives
@@ -2380,6 +2387,10 @@
             llarena.arena_free(arena)
             self.rawmalloced_total_size -= r_uint(allocsize)
 
+    def start_free(self):
+        self.ac.mass_free_prepare()
+        self.start_free_rawmalloc_objects()
+
     def start_free_rawmalloc_objects(self):
         ll_assert(not self.raw_malloc_might_sweep.non_empty(),
                   "raw_malloc_might_sweep must be empty")
@@ -3036,6 +3047,9 @@
 
     # Methods meant to be overridden by subclasses that store flags elsewhere.
 
+    def finalize_header(self, hdr):
+        """Clean up hdr before the object is freed."""
+
     def copy_header(self, src, dest):
         self.header(dest).tid = self.header(src).tid
 
diff --git a/rpython/memory/gc/incminimark_remoteheader.py b/rpython/memory/gc/incminimark_remoteheader.py
--- a/rpython/memory/gc/incminimark_remoteheader.py
+++ b/rpython/memory/gc/incminimark_remoteheader.py
@@ -1,19 +1,35 @@
 """Incminimark with GC flags stored in a separate page for fork-friendliness."""
 
+from rpython.rtyper.lltypesystem import llarena
 from rpython.memory.gc import incminimark
-from rpython.rtyper.lltypesystem import lltype, llmemory
+from rpython.rlib.rarithmetic import LONG_BIT
+from rpython.rtyper.lltypesystem import rffi, lltype, llmemory
+
+SIGNEDP = lltype.Ptr(lltype.FixedSizeArray(lltype.Signed, 1))
 
 class IncrementalMiniMarkRemoteHeaderGC(incminimark.IncrementalMiniMarkGC):
     # The GC header is similar to incminimark, except that the flags can be
     # placed anywhere, not just in the bits of tid.
-    # TODO: Actually place flags somewhere other than tid.
     HDR = lltype.Struct('header',
                         ('tid', lltype.Signed),
-                        ('remote_flags', lltype.Ptr(lltype.FixedSizeArray(lltype.Signed, 1))))
+                        ('remote_flags', SIGNEDP))
 
-    def init_gc_object(self, addr, typeid16, flags=0):
-        super(IncrementalMiniMarkRemoteHeaderGC, self).init_gc_object(addr, typeid16, flags)
-        hdr = llmemory.cast_adr_to_ptr(addr, lltype.Ptr(self.HDR))
+    def __init__(self, config, **kwargs):
+        super(IncrementalMiniMarkRemoteHeaderGC, self).__init__(config, **kwargs)
+        ArenaCollectionClass = kwargs.get('ArenaCollectionClass', None)
+        if ArenaCollectionClass is None:
+            from rpython.memory.gc import minimarkpage
+            ArenaCollectionClass = minimarkpage.ArenaCollection
+
+        # TODO: can I reuse self.ac somehow? Is there a better thing to use?
+        # This seems absurd.
+        self.__ac_for_flags = ArenaCollectionClass(
+                64*incminimark.WORD, 16*incminimark.WORD,
+                small_request_threshold=LONG_BIT)
+
+    def init_gc_object(self, adr, typeid16, flags=0):
+        super(IncrementalMiniMarkRemoteHeaderGC, self).init_gc_object(adr, typeid16, flags)
+        hdr = llmemory.cast_adr_to_ptr(adr, lltype.Ptr(self.HDR))
         hdr.remote_flags = lltype.direct_fieldptr(hdr, 'tid')
 
     def make_forwardstub(self, obj, forward_to):
@@ -28,7 +44,42 @@
         dest_hdr = self.header(dest)
         dest_hdr.tid = self.get_flags(src)
         dest_hdr.remote_flags = lltype.direct_fieldptr(dest_hdr, 'tid')
-        # TODO: make new remote flag sometimes.
+        self.__extract_flags_to_pointer(dest_hdr)
+
+    def __extract_flags_to_pointer(self, hdr):
+        """Make an object's GC header use out-of-line flags.
+
+        Expects the object to not use inline tid-flags.
+        """
+        assert (hdr.remote_flags == lltype.nullptr(SIGNEDP.TO)
+                or hdr.remote_flags == lltype.direct_fieldptr(hdr, 'tid')), \
+                    "leaking old remote_flags!"
+        size = llmemory.sizeof(lltype.Signed)
+        adr = self.__ac_for_flags.malloc(size)
+        hdr.remote_flags = llmemory.cast_adr_to_ptr(adr, SIGNEDP)
+        hdr.remote_flags[0] = hdr.tid
+
+    def finalize_header(self, adr):
+        hdr = llmemory.cast_adr_to_ptr(adr, lltype.Ptr(self.HDR))
+        if hdr.remote_flags != lltype.nullptr(SIGNEDP.TO):
+            # If it points to allocated memory, this will be picked up by
+            # __free_flags_if_finalized.
+            hdr.remote_flags[0] |= incminimark.GCFLAG_DEAD
+
+    def __free_flags_if_finalized(self, adr):
+        flag_ptr = llmemory.cast_adr_to_ptr(adr, SIGNEDP)
+        # If -42, it was set in finalize_header and the object was freed.
+        return flag_ptr[0] & incminimark.GCFLAG_DEAD
+
+    def free_unvisited_arena_objects_step(self, limit):
+        done = super(IncrementalMiniMarkRemoteHeaderGC, self).free_unvisited_arena_objects_step(limit)
+        self.__ac_for_flags.mass_free_incremental(
+            self.__free_flags_if_finalized, done)
+        return done
+
+    def start_free(self):
+        super(IncrementalMiniMarkRemoteHeaderGC, self).start_free()
+        self.__ac_for_flags.mass_free_prepare()
 
     # Manipulate flags through a pointer.
 
diff --git a/rpython/memory/gc/test/test_direct.py b/rpython/memory/gc/test/test_direct.py
--- a/rpython/memory/gc/test/test_direct.py
+++ b/rpython/memory/gc/test/test_direct.py
@@ -537,26 +537,24 @@
         #
         addr_src = llmemory.cast_ptr_to_adr(p_src)
         addr_dst = llmemory.cast_ptr_to_adr(p_dst)
-        hdr_src = self.gc.header(addr_src)
-        hdr_dst = self.gc.header(addr_dst)
         #
-        assert hdr_src.tid & minimark.GCFLAG_TRACK_YOUNG_PTRS
-        assert hdr_dst.tid & minimark.GCFLAG_TRACK_YOUNG_PTRS
+        assert self.gc.get_flags(addr_src) & minimark.GCFLAG_TRACK_YOUNG_PTRS
+        assert self.gc.get_flags(addr_dst) & minimark.GCFLAG_TRACK_YOUNG_PTRS
         #
         res = self.gc.writebarrier_before_copy(addr_src, addr_dst, 0, 0, 10)
         assert res
-        assert hdr_dst.tid & minimark.GCFLAG_TRACK_YOUNG_PTRS
+        assert self.gc.get_flags(addr_dst) & minimark.GCFLAG_TRACK_YOUNG_PTRS
         #
-        hdr_src.tid &= ~minimark.GCFLAG_TRACK_YOUNG_PTRS  # pretend we have young ptrs
+        self.gc.remove_flags(addr_src, minimark.GCFLAG_TRACK_YOUNG_PTRS)  # pretend we have young ptrs
         res = self.gc.writebarrier_before_copy(addr_src, addr_dst, 0, 0, 10)
         assert res # we optimized it
-        assert hdr_dst.tid & minimark.GCFLAG_TRACK_YOUNG_PTRS == 0 # and we copied the flag
+        assert self.gc.get_flags(addr_dst) & minimark.GCFLAG_TRACK_YOUNG_PTRS == 0 # and we copied the flag
         #
-        hdr_src.tid |= minimark.GCFLAG_TRACK_YOUNG_PTRS
-        hdr_dst.tid |= minimark.GCFLAG_TRACK_YOUNG_PTRS
-        hdr_src.tid |= minimark.GCFLAG_HAS_CARDS
-        hdr_src.tid |= minimark.GCFLAG_CARDS_SET
-        # hdr_dst.tid does not have minimark.GCFLAG_HAS_CARDS
+        self.gc.add_flags(addr_src, minimark.GCFLAG_TRACK_YOUNG_PTRS)
+        self.gc.add_flags(addr_dst, minimark.GCFLAG_TRACK_YOUNG_PTRS)
+        self.gc.add_flags(addr_src, minimark.GCFLAG_HAS_CARDS)
+        self.gc.add_flags(addr_src, minimark.GCFLAG_CARDS_SET)
+        # addr_dst flags don't not have minimark.GCFLAG_HAS_CARDS
         res = self.gc.writebarrier_before_copy(addr_src, addr_dst, 0, 0, 10)
         assert not res # there might be young ptrs, let ll_arraycopy to find them
 
@@ -567,18 +565,16 @@
         self.gc.next_major_collection_threshold = 99999.0
         addr_src = self.gc.external_malloc(tid, largeobj_size, alloc_young=True)
         addr_dst = self.gc.external_malloc(tid, largeobj_size, alloc_young=True)
-        hdr_src = self.gc.header(addr_src)
-        hdr_dst = self.gc.header(addr_dst)
         #
-        assert hdr_src.tid & minimark.GCFLAG_HAS_CARDS
-        assert hdr_dst.tid & minimark.GCFLAG_HAS_CARDS
+        assert self.gc.get_flags(addr_src) & minimark.GCFLAG_HAS_CARDS
+        assert self.gc.get_flags(addr_dst) & minimark.GCFLAG_HAS_CARDS
         #
         self.gc.write_barrier_from_array(addr_src, 0)
         index_in_third_page = int(2.5 * self.gc.card_page_indices)
         assert index_in_third_page < largeobj_size
         self.gc.write_barrier_from_array(addr_src, index_in_third_page)
         #
-        assert hdr_src.tid & minimark.GCFLAG_CARDS_SET
+        assert self.gc.get_flags(addr_src) & minimark.GCFLAG_CARDS_SET
         addr_byte = self.gc.get_card(addr_src, 0)
         assert ord(addr_byte.char[0]) == 0x01 | 0x04  # bits 0 and 2
         #
@@ -586,7 +582,7 @@
                                              0, 0, 2*self.gc.card_page_indices)
         assert res
         #
-        assert hdr_dst.tid & minimark.GCFLAG_CARDS_SET
+        assert self.gc.get_flags(addr_dst) & minimark.GCFLAG_CARDS_SET
         addr_byte = self.gc.get_card(addr_dst, 0)
         assert ord(addr_byte.char[0]) == 0x01 | 0x04  # bits 0 and 2
 
@@ -607,20 +603,20 @@
             self.stackroots.append(curobj)
 
 
-        oldobj = self.stackroots[-1]
-        oldhdr = self.gc.header(llmemory.cast_ptr_to_adr(oldobj))
+        oldadr = llmemory.cast_ptr_to_adr(self.stackroots[-1])
 
-        assert oldhdr.tid & incminimark.GCFLAG_VISITED == 0
+        assert self.gc.get_flags(oldadr) & incminimark.GCFLAG_VISITED == 0
         self.gc.debug_gc_step_until(incminimark.STATE_MARKING)
         oldobj = self.stackroots[-1]
+        oldadr = llmemory.cast_ptr_to_adr(oldobj)
         # object shifted by minor collect
-        oldhdr = self.gc.header(llmemory.cast_ptr_to_adr(oldobj))
-        assert oldhdr.tid & incminimark.GCFLAG_VISITED == 0
+        oldhdr = self.gc.header(oldadr)
+        assert self.gc.get_flags(oldadr) & incminimark.GCFLAG_VISITED == 0
 
         self.gc._minor_collection()
         self.gc.visit_all_objects_step(1)
 
-        assert oldhdr.tid & incminimark.GCFLAG_VISITED
+        assert self.gc.get_flags(oldadr) & incminimark.GCFLAG_VISITED
 
         #at this point the first object should have been processed
         newobj = self.malloc(S)
@@ -641,8 +637,8 @@
 
         self.gc.debug_gc_step_until(incminimark.STATE_SWEEPING)
         oldobj = self.stackroots[-1]
-        oldhdr = self.gc.header(llmemory.cast_ptr_to_adr(oldobj))
-        assert oldhdr.tid & incminimark.GCFLAG_VISITED
+        oldadr = llmemory.cast_ptr_to_adr(oldobj)
+        assert self.gc.get_flags(oldadr) & incminimark.GCFLAG_VISITED
 
         newobj1 = self.malloc(S)
         newobj2 = self.malloc(S)


More information about the pypy-commit mailing list