[pypy-commit] pypy stm-gc: Progress

arigo noreply at buildbot.pypy.org
Sun Apr 15 15:09:58 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: stm-gc
Changeset: r54369:ddb7a8bdd471
Date: 2012-04-15 15:09 +0200
http://bitbucket.org/pypy/pypy/changeset/ddb7a8bdd471/

Log:	Progress

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
@@ -44,6 +44,7 @@
 GCFLAG_FIXED_HASH = first_gcflag << 3
 GCFLAG_WEAKREF    = first_gcflag << 4
 GCFLAG_VISITED    = first_gcflag << 5
+GCFLAG_MOVED      = first_gcflag << 6
 
 
 def always_inline(fn):
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
@@ -27,27 +27,22 @@
         self.chained_list = NULL
         self.special_stack = self.gc.AddressStack()
 
-    def delete(self):
-        self.special_stack.delete()
-        free_non_gc_object(self)
+    def malloc_object(self, totalsize):
+        """Malloc.  You must also call add_regular() or add_special() later."""
+        adr1 = llarena.arena_malloc(llmemory.raw_malloc_usage(totalsize), 0)
+        llarena.arena_reserve(adr1, totalsize)
+        return adr1 + self.gc.gcheaderbuilder.size_gc_header
 
-    def malloc_regular(self, size):
-        """Malloc for an object where the 'version' field can be used
-        internally for a chained list."""
-        adr1 = llarena.arena_malloc(size, 0)
-        adr2 = adr1 + self.gc.gcheaderbuilder
-        hdr = llmemory.cast_adr_to_ptr(adr1, lltype.Ptr(self.gc.HDR))
+    def add_regular(self, obj):
+        """After malloc_object(), register the object in the internal chained
+        list.  For objects whose 'version' field is not otherwise needed."""
+        hdr = self.gc.header(obj)
         hdr.version = self.chained_list
-        self.chained_list = adr2
-        return adr2
+        self.chained_list = obj
 
-    def malloc_special(self, size):
-        """Malloc for an object where the 'version' field cannot be
-        used internally.  It's the rare case here."""
-        adr1 = llarena.arena_malloc(size, 0)
-        adr2 = adr1 + self.gc.gcheaderbuilder.size_gc_header
-        self.special_stack.append(adr2)
-        return adr2
+    def add_special(self, obj):
+        """After malloc_object(), register the object in a separate stack."""
+        self.special_stack.append(obj)
 
     def free_object(self, adr2):
         adr1 = adr2 - self.gc.gcheaderbuilder.size_gc_header
@@ -56,3 +51,18 @@
     def replace_special_stack(self, new_special_stack):
         self.special_stack.delete()
         self.special_stack = new_special_stack
+
+    def clear(self):
+        obj = self.chained_list
+        self.chained_list = NULL
+        while obj:
+            next = self.gc.header(obj).version
+            self.free_object(obj)
+            obj = next
+        s = self.special_stack
+        while s.non_empty():
+            self.free_object(s.pop())
+
+    def delete(self):
+        self.special_stack.delete()
+        free_non_gc_object(self)
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
@@ -8,6 +8,7 @@
 from pypy.rpython.memory.gc.stmgc import WORD, NULL
 from pypy.rpython.memory.gc.stmgc import always_inline, dont_inline
 from pypy.rpython.memory.gc.stmgc import GCFLAG_GLOBAL, GCFLAG_VISITED
+from pypy.rpython.memory.gc.stmgc import GCFLAG_MOVED
 
 
 class StmGCTLS(object):
@@ -19,7 +20,6 @@
     nontranslated_dict = {}
 
     def __init__(self, gc, in_main_thread):
-        from pypy.rpython.memory.gc.stmshared import StmGCThreadLocalAllocator
         self.gc = gc
         self.in_main_thread = in_main_thread
         self.stm_operations = self.gc.stm_operations
@@ -46,7 +46,7 @@
         #self.old_objects_with_weakrefs = NULL
         #
         # --- a thread-local allocator for the shared area
-        self.sharedarea_tls = StmGCThreadLocalAllocator(gc.sharedarea)
+        self.sharedarea_tls = None
         #
         self._register_with_C_code()
 
@@ -153,37 +153,41 @@
         #
         debug_start("gc-local")
         #
-        # Linked list of LOCAL objects pending a visit.  Note that no
-        # GLOBAL object can at any point contain a reference to a LOCAL
-        # object.
-        self.pending_list = NULL
+        # Move away the previous sharedarea_tls and start a new one.
+        from pypy.rpython.memory.gc.stmshared import StmGCThreadLocalAllocator
+        previous_sharedarea_tls = self.sharedarea_tls    # may be None
+        self.sharedarea_tls = StmGCThreadLocalAllocator(self.gc.sharedarea)
+        #
+        # List of LOCAL objects pending a visit.  Note that no GLOBAL
+        # object can at any point contain a reference to a LOCAL object.
+        self.pending = self.AddressStack()
         #
         # First, find the roots that point to LOCAL objects.  All YOUNG
         # (i.e. nursery) objects found are copied out of the nursery.
-        # All OLD objects found are flagged with GCFLAG_VISITED.  At this
-        # point, the content of the objects is not modified; the objects
-        # are merely added to the chained list 'pending_list'.
-        self.collect_roots_in_nursery()
+        # All OLD objects found are flagged with GCFLAG_VISITED.
+        # At this point, the content of the objects is not modified;
+        # they are simply added to 'pending'.
+        self.collect_roots_from_stack()
         #
         # Also find the roots that are the local copy of GCFLAG_WAS_COPIED
         # objects.
         self.collect_roots_from_tldict()
         #
-        # Now repeat following objects until 'pending_list' is empty.
-        self.collect_oldrefs_to_nursery()
+        # 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()
         #
-        # Ask the ArenaCollection to visit all objects.  Free the ones
-        # that have not been visited above, and reset GCFLAG_VISITED on
-        # the others.
-        self.ac.mass_free(self._free_if_unvisited)
+        # Visit all previous OLD objects.  Free the ones that have not been
+        # visited above, and reset GCFLAG_VISITED on the others.
+        if previous_sharedarea_tls is not None:
+            self.mass_free_old_local(previous_sharedarea_tls)
         #
         # All live nursery objects are out, and the rest dies.  Fill
         # the whole nursery with zero and reset the current nursery pointer.
-        llarena.arena_reset(self.nursery, self.nursery_size, 2)
+        llarena.arena_reset(self.nursery_start, self.nursery_size, 2)
         self.nursery_free = self.nursery_start
         #
         debug_stop("gc-local")
@@ -215,7 +219,7 @@
     def allocate_object_of_size(self, size):
         if not self.nursery_free:
             fatalerror("malloc in a non-main thread but outside a transaction")
-        if size > self.nursery_size // 8 * 7:
+        if llmemory.raw_malloc_usage(size) > self.nursery_size // 8 * 7:
             fatalerror("object too large to ever fit in the nursery")
         while True:
             self.local_collection()
@@ -225,6 +229,11 @@
                 continue         # try again
             return free
 
+    def is_in_nursery(self, addr):
+        ll_assert(llmemory.cast_adr_to_int(addr) & 1 == 0,
+                  "odd-valued (i.e. tagged) pointer unexpected here")
+        return self.nursery_start <= addr < self.nursery_top
+
     # ------------------------------------------------------------
 
     def _promote_locals_to_globals(self):
@@ -245,5 +254,102 @@
 ##            obj = hdr.version
 
     def _cleanup_state(self):
-        if self.rawmalloced_objects:
-            xxx     # free the rawmalloced_objects still around
+        #if self.rawmalloced_objects:
+        #    xxx     # free the rawmalloced_objects still around
+
+        # if we still have a StmGCThreadLocalAllocator, free the old unused
+        # local objects it still contains
+        if self.sharedarea_tls is not None:
+            self.sharedarea_tls.clear()
+            self.sharedarea_tls.delete()
+            self.sharedarea_tls = None
+
+
+    def collect_roots_from_stack(self):
+        self.gc.root_walker.walk_roots(
+            StmGCTLS._trace_drag_out1,  # stack roots of the current thread
+            None,                       # static in prebuilt non-gc
+            None,                       # static in prebuilt gc
+            self)
+
+    def _trace_drag_out1(self, root):
+        self._trace_drag_out(root, None)
+
+    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
+        fix the pointer.  Also, add the object to 'pending_list', if it was
+        not done so far.
+        """
+        obj = root.address[0]
+        hdr = self.gc.header(obj)
+        #
+        # If 'obj' is not in the nursery, we set GCFLAG_VISITED
+        if not self.is_in_nursery(obj):
+            if hdr.tid & GCFLAG_VISITED == 0:
+                hdr.tid |= GCFLAG_VISITED
+                self.pending.append(obj)
+            return
+        #
+        # If 'obj' was already forwarded, change it to its forwarding address.
+        if hdr.tid & GCFLAG_MOVED:
+            root.address[0] = hdr.version
+            return
+        #
+        # First visit to 'obj': we must move this YOUNG obj out of the nursery.
+        size_gc_header = self.gc.gcheaderbuilder.size_gc_header
+        size = self.gc.get_size(obj)
+        totalsize = size_gc_header + size
+        #
+        # Common case: allocate a new nonmovable location for it.
+        newobj = self._malloc_out_of_nursery(totalsize)
+        #
+        # Copy it.  Note that references to other objects in the
+        # nursery are kept unchanged in this step.
+        llmemory.raw_memcopy(obj - size_gc_header,
+                             newobj - size_gc_header,
+                             totalsize)
+        #
+        # Set the YOUNG copy's GCFLAG_MOVED and set its version to
+        # point to the OLD copy.
+        hdr.tid |= GCFLAG_MOVED
+        hdr.version = newobj
+        #
+        # Change the original pointer to this object.
+        root.address[0] = newobj
+        #
+        # Add the newobj to the list 'pending', because it can contain
+        # further pointers to other young objects.  We will fix such
+        # references to point to the copy of the young objects when we
+        # walk 'pending_list'.
+        self.pending.append(newobj)
+
+    def _malloc_out_of_nursery(self, totalsize):
+        obj = self.sharedarea_tls.malloc_object(totalsize)
+        self.sharedarea_tls.add_regular(obj)
+        return obj
+
+    def collect_roots_from_tldict(self):
+        pass  # XXX
+
+    def collect_flush_pending(self):
+        # Follow the objects in the 'pending' stack and move the
+        # young objects they point to out of the nursery.
+        while self.pending.non_empty():
+            obj = self.pending.pop()
+            self.gc.trace(obj, self._trace_drag_out, None)
+
+    def mass_free_old_local(self, previous_sharedarea_tls):
+        obj = previous_sharedarea_tls.chained_list
+        previous_sharedarea_tls.delete()
+        while obj != NULL:
+            hdr = self.gc.header(obj)
+            next = hdr.version
+            if hdr.tid & GCFLAG_VISITED:
+                # survives: relink in the new sharedarea_tls
+                self.sharedarea_tls.add_regular(obj)
+            else:
+                # dies
+                self.sharedarea_tls.free_object(obj)
+            #
+            obj = next
diff --git a/pypy/rpython/memory/gc/test/test_stmtls.py b/pypy/rpython/memory/gc/test/test_stmtls.py
--- a/pypy/rpython/memory/gc/test/test_stmtls.py
+++ b/pypy/rpython/memory/gc/test/test_stmtls.py
@@ -1,10 +1,12 @@
 import py
-from pypy.rpython.lltypesystem import lltype, llmemory, llarena, llgroup, rffi
+from pypy.rpython.lltypesystem import lltype, llmemory, llarena, llgroup
 from pypy.rpython.memory.gc.stmtls import StmGCTLS, WORD
 from pypy.rpython.memory.gc.test.test_stmgc import StmGCTests
 from pypy.rpython.memory.support import get_address_stack, get_address_deque
+from pypy.rpython.memory.gcheader import GCHeaderBuilder
 
 
+NULL = llmemory.NULL
 S = lltype.GcStruct('S', ('a', lltype.Signed), ('b', lltype.Signed),
                          ('c', lltype.Signed))
 
@@ -18,6 +20,24 @@
 class FakeSharedArea:
     pass
 
+class FakeRootWalker:
+    def walk_roots(self, f1, f2, f3, arg):
+        if f1 is not None:
+            A = lltype.Array(llmemory.Address)
+            roots = lltype.malloc(A, len(self.current_stack), flavor='raw')
+            for i in range(len(self.current_stack)):
+                roots[i] = llmemory.cast_ptr_to_adr(self.current_stack[i])
+            for i in range(len(self.current_stack)):
+                root = lltype.direct_ptradd(lltype.direct_arrayitems(roots), i)
+                root = llmemory.cast_ptr_to_adr(root)
+                f1(arg, root)
+            for i in range(len(self.current_stack)):
+                P = lltype.typeOf(self.current_stack[i])
+                self.current_stack[i] = llmemory.cast_adr_to_ptr(roots[i], P)
+            lltype.free(roots, flavor='raw')
+        assert f2 is None
+        assert f3 is None
+
 class FakeGC:
     from pypy.rpython.memory.support import AddressDict, null_address_dict
     AddressStack = get_address_stack()
@@ -25,6 +45,28 @@
     nursery_size = 128
     stm_operations = FakeStmOperations()
     sharedarea = FakeSharedArea()
+    root_walker = FakeRootWalker()
+    HDR = lltype.Struct('header', ('tid', lltype.Signed),
+                                  ('version', llmemory.Address))
+    gcheaderbuilder = GCHeaderBuilder(HDR)
+
+    def header(self, addr):
+        addr -= self.gcheaderbuilder.size_gc_header
+        return llmemory.cast_adr_to_ptr(addr, lltype.Ptr(self.HDR))
+
+    def get_size(self, addr):
+        return llmemory.sizeof(lltype.typeOf(addr.ptr).TO)
+
+    def trace(self, obj, callback, arg):
+        TYPE = obj.ptr._TYPE.TO
+        if TYPE == S:
+            ofslist = []     # no pointers in S
+        else:
+            assert 0
+        for ofs in ofslist:
+            addr = obj + ofs
+            if addr.address[0]:
+                callback(addr, arg)
 
 
 class TestStmGCTLS(object):
@@ -36,6 +78,8 @@
         self.gctls_main = StmGCTLS(self.gc, in_main_thread=True)
         self.gctls_thrd = StmGCTLS(self.gc, in_main_thread=False)
         self.gc.main_thread_tls = self.gctls_main
+        self.gctls_main.start_transaction()
+        self.gc.root_walker.current_stack = self.current_stack
 
     def stack_add(self, p):
         self.current_stack.append(p)
@@ -43,6 +87,20 @@
     def stack_pop(self):
         return self.current_stack.pop()
 
+    def malloc(self, STRUCT):
+        size = llarena.round_up_for_allocation(llmemory.sizeof(STRUCT))
+        size_gc_header = self.gc.gcheaderbuilder.size_gc_header
+        totalsize = size_gc_header + size
+        tls = self.gc.main_thread_tls
+        adr = tls.allocate_bump_pointer(totalsize)
+        #
+        llarena.arena_reserve(adr, totalsize)
+        obj = adr + size_gc_header
+        hdr = self.gc.header(obj)
+        hdr.tid = 0
+        hdr.version = NULL
+        return llmemory.cast_adr_to_ptr(obj, lltype.Ptr(STRUCT))
+
     # ----------
 
     def test_creation_works(self):
@@ -59,16 +117,19 @@
         assert a6 - a5 == 5
 
     def test_local_collection(self):
-        s1, _ = self.malloc(S); s1.a = 111
-        s2, _ = self.malloc(S); s2.a = 222
+        s1 = self.malloc(S); s1.a = 111
+        s2 = self.malloc(S); s2.a = 222
         self.stack_add(s2)
         self.gc.main_thread_tls.local_collection()
         s3 = self.stack_pop()
         assert s3.a == 222
-        xxxx # raises...
-        s1.a
-        s2.a
+        py.test.raises(RuntimeError, "s1.a")
+        py.test.raises(RuntimeError, "s2.a")
 
-    def test_alloc_a_lot(self):
-        for i in range(1000):
-            sr1, sr1_adr = self.malloc(SR)
+    def test_alloc_a_lot_nonkept(self):
+        for i in range(100):
+            self.malloc(S)
+
+    def test_alloc_a_lot_kept(self):
+        for i in range(100):
+            self.stack_add(self.malloc(S))
diff --git a/pypy/rpython/memory/gctransform/framework.py b/pypy/rpython/memory/gctransform/framework.py
--- a/pypy/rpython/memory/gctransform/framework.py
+++ b/pypy/rpython/memory/gctransform/framework.py
@@ -1374,16 +1374,19 @@
 
     def walk_roots(self, collect_stack_root,
                    collect_static_in_prebuilt_nongc,
-                   collect_static_in_prebuilt_gc):
+                   collect_static_in_prebuilt_gc,
+                   arg=None):
         gcdata = self.gcdata
         gc = self.gc
+        if arg is None:
+            arg = gc
         if collect_static_in_prebuilt_nongc:
             addr = gcdata.static_root_start
             end = gcdata.static_root_nongcend
             while addr != end:
                 result = addr.address[0]
                 if gc.points_to_valid_gc_object(result):
-                    collect_static_in_prebuilt_nongc(gc, result)
+                    collect_static_in_prebuilt_nongc(arg, result)
                 addr += sizeofaddr
         if collect_static_in_prebuilt_gc:
             addr = gcdata.static_root_nongcend
@@ -1391,10 +1394,10 @@
             while addr != end:
                 result = addr.address[0]
                 if gc.points_to_valid_gc_object(result):
-                    collect_static_in_prebuilt_gc(gc, result)
+                    collect_static_in_prebuilt_gc(arg, result)
                 addr += sizeofaddr
         if collect_stack_root:
-            self.walk_stack_roots(collect_stack_root)     # abstract
+            self.walk_stack_roots(collect_stack_root, arg)     # abstract
 
     def need_stacklet_support(self):
         raise Exception("%s does not support stacklets" % (
diff --git a/pypy/rpython/memory/gctransform/shadowstack.py b/pypy/rpython/memory/gctransform/shadowstack.py
--- a/pypy/rpython/memory/gctransform/shadowstack.py
+++ b/pypy/rpython/memory/gctransform/shadowstack.py
@@ -28,7 +28,7 @@
         self.decr_stack = decr_stack
 
         root_iterator = get_root_iterator(gctransformer)
-        def walk_stack_root(callback, start, end):
+        def walk_stack_root(callback, arg, start, end):
             root_iterator.setcontext(NonConstant(llmemory.NULL))
             gc = self.gc
             addr = end
@@ -36,7 +36,7 @@
                 addr = root_iterator.nextleft(gc, start, addr)
                 if addr == llmemory.NULL:
                     return
-                callback(gc, addr)
+                callback(arg, addr)
         self.rootstackhook = walk_stack_root
 
         self.shadow_stack_pool = ShadowStackPool(gcdata)
@@ -56,9 +56,9 @@
         self.shadow_stack_pool.initial_setup()
         BaseRootWalker.setup_root_walker(self)
 
-    def walk_stack_roots(self, collect_stack_root):
+    def walk_stack_roots(self, collect_stack_root, arg):
         gcdata = self.gcdata
-        self.rootstackhook(collect_stack_root,
+        self.rootstackhook(collect_stack_root, arg,
                            gcdata.root_stack_base, gcdata.root_stack_top)
 
     def need_thread_support(self, gctransformer, getfn):


More information about the pypy-commit mailing list