Commits

Armin Rigo committed 6e55716

Tentative. When a transaction finishes, save in the shadowstack which
objects were locals (using the bit '1<<1'). When a transaction starts,
remake the corresponding objects locals.

Comments (0)

Files changed (3)

pypy/rpython/memory/gc/stmtls.py

         # malloced objects will be LOCAL.
         if self.gc.DEBUG:
             self.check_all_global_objects()
+        self.relocalize_from_stack()
 
     def stop_transaction(self):
         """Stop a transaction: do a local collection to empty the
         nursery and track which objects are still alive now, and
         then mark all these objects as global."""
-        self.local_collection(end_of_transaction=1)
+        self.local_collection(end_of_transaction=True)
         if not self.local_nursery_is_empty():
-            self.local_collection(end_of_transaction=1, run_finalizers=False)
+            self.local_collection(end_of_transaction=True,
+                                  run_finalizers=False)
         self._promote_locals_to_globals()
         self._disable_mallocs()
 
 
     # ------------------------------------------------------------
 
-    def local_collection(self, end_of_transaction=0, run_finalizers=True):
+    def local_collection(self, end_of_transaction=False, run_finalizers=True):
         """Do a local collection.  This should be equivalent to a minor
         collection only, but the GC is not generational so far, so it is
         for now the same as a full collection --- but only on LOCAL
         # 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()
+        self.collect_roots_from_stack(end_of_transaction)
         #
         # Find the roots that are living in raw structures.
         self.collect_from_raw_structures()
             self.sharedarea_tls.free_object(lst.pop())
 
 
-    def collect_roots_from_stack(self):
+    def collect_roots_from_stack(self, end_of_transaction):
+        if end_of_transaction:
+            # in this mode, we flag the reference to local objects in order
+            # to re-localize them when we later start the next transaction
+            # using this section of the shadowstack
+            self.gc.root_walker.walk_current_stack_roots(
+                StmGCTLS._trace_drag_out_and_flag_local, self)
+        else:
+            self.gc.root_walker.walk_current_stack_roots(
+                StmGCTLS._trace_drag_out1, self)
+
+    def _trace_drag_out_and_flag_local(self, root):
+        x = llmemory.cast_adr_to_int(root.address[0])
+        if x & 2:
+            root.address[0] -= 2
+        self._trace_drag_out1(root)
+        obj = root.address[0]
+        if self.gc.header(obj).tid & GCFLAG_GLOBAL == 0:
+            x = llmemory.cast_adr_to_int(obj)
+            ll_assert(x & 3 == 0, "flag_local: misaligned obj")
+            obj = llarena.getfakearenaaddress(obj)
+            root.address[0] = obj + 2
+
+    def relocalize_from_stack(self):
         self.gc.root_walker.walk_current_stack_roots(
-            StmGCTLS._trace_drag_out1, self)
+            StmGCTLS._relocalize_from_stack, self)
+
+    def _relocalize_from_stack(self, root):
+        x = llmemory.cast_adr_to_int(root.address[0])
+        if x & 2:
+            obj = root.address[0] - 2
+            localobj = self.gc.stm_writebarrier(obj)
+            root.address[0] = localobj
 
     def collect_from_raw_structures(self):
         self.gc.root_walker.walk_current_nongc_roots(
 
     # ------------------------------------------------------------
 
-    def _debug_check_all_global1(self, root):
-        self._debug_check_all_global(root, None)
+    def _debug_check_all_global_from_stack_1(self, root):
+        obj = root.address[0]
+        if llmemory.cast_adr_to_int(obj) & 2:
+            obj -= 2     # will be fixed by relocalize_from_stack()
+        self._debug_check_all_global_obj(obj)
 
     def _debug_check_all_global(self, root, ignored):
         obj = root.address[0]
+        self._debug_check_all_global_obj(obj)
+
+    def _debug_check_all_global_obj(self, obj):
         if self.debug_seen.contains(obj):
             return
         hdr = self.gc.header(obj)
         self.pending = self.AddressStack()
         self.debug_seen = self.AddressDict()
         self.gc.root_walker.walk_current_stack_roots(
-            StmGCTLS._debug_check_all_global1, self)
+            StmGCTLS._debug_check_all_global_from_stack_1, self)
         while self.pending.non_empty():
             obj = self.pending.pop()
             offset = self.gc.weakpointer_offset(self.gc.get_type_id(obj))

pypy/rpython/memory/gc/test/test_stmgc.py

             shapes = [shape_of_reachable(obj) for obj in root_objects]
             #
             # Do a minor collection
-            self.gc.root_walker.current_stack = fromstack[:]
+            for p in fromstack:
+                self.gc.root_walker.push(p)
             self.gc.collect(0)
             #
             # Reload 'fromstack', which may have moved, and compare the shape
             # of the graph of reachable objects now
-            fromstack[:] = self.gc.root_walker.current_stack
+            for i in range(len(fromstack)-1, -1, -1):
+                fromstack[i] = self.gc.root_walker.pop()
             root_objects = prebuilt + fromstack
             shapes2 = [shape_of_reachable(obj, can_be_indirect=False)
                        for obj in root_objects]
         tr2, tr2_adr = self.malloc(SR, globl=True)
         tr3, tr3_adr = self.malloc(SR, globl=True)
         tr1.sr2 = tr2
-        self.gc.root_walker.current_stack = [tr1]
+        self.gc.root_walker.push(tr1)
         sr1_adr = self.gc.stm_writebarrier(tr1_adr)
         assert sr1_adr != tr1_adr
         sr2_adr = self.gc.stm_writebarrier(tr2_adr)
         self.checkflags(tr2_adr, True, True)     # tr2 has become global again
         self.checkflags(tr3_adr, True, True)     # tr3 has become global again
 
+    def test_obj_with_invalid_offset_after_transaction_stop(self):
+        from pypy.rpython.memory.gc.test import test_stmtls
+        self.gc.root_walker = test_stmtls.FakeRootWalker()
+        #
+        tr1, tr1_adr = self.malloc(SR, globl=False)  # local
+        self.checkflags(tr1_adr, False, False)    # check that it is local
+        self.gc.root_walker.push(tr1)
+        self.gc.stop_transaction()
+        # now tr1 is stored in the shadowstack with an offset of 2 to mark
+        # that it was local.
+        py.test.raises(llarena.ArenaError, self.gc.root_walker.pop)
+
     def test_non_prebuilt_relocalize_after_transaction_break(self):
         from pypy.rpython.memory.gc.test import test_stmtls
         self.gc.root_walker = test_stmtls.FakeRootWalker()
         self.checkflags(tr1_adr, False, False)    # check that it is local
         self.checkflags(tr2_adr, False, False)    # check that it is local
         tr1.sr2 = tr2
-        self.gc.root_walker.current_stack = [tr1]
+        self.gc.root_walker.push(tr1)
         self.gc.stop_transaction()
+        self.gc.start_transaction()
         # tr1 and tr2 moved out of the nursery: check that
-        [sr1] = self.gc.root_walker.current_stack
+        sr1 = self.gc.root_walker.pop()
         assert sr1._obj0 != tr1._obj0
         sr2 = sr1.sr2
         assert sr2 and sr2 != sr1 and not sr2.sr2
         assert sr2._obj0 != tr2._obj0
         sr1_adr = llmemory.cast_ptr_to_adr(sr1)
         sr2_adr = llmemory.cast_ptr_to_adr(sr2)
-        self.checkflags(sr1_adr, True, False)     # sr1 is a global
+        self.checkflags(sr1_adr, False, True)     # sr1 is a WAS_COPIED local
         self.checkflags(sr2_adr, True, False)     # sr2 is a global
-        self.gc.start_transaction()
-        self.checkflags(sr1_adr, True, False)     # sr1 is still global
-        self.checkflags(sr2_adr, True, False)     # sr2 is still global
 
     def test_collect_from_main_thread_was_global_objects(self):
         tr1, tr1_adr = self.malloc(SR, globl=True)  # a global prebuilt object
         wr1, wr1_adr = self.malloc(WR, globl=False, weakref=True)
         wr1.wadr = sr1_adr
         #
-        self.gc.root_walker.current_stack = [wr1]
+        self.gc.root_walker.push(wr1)
         self.gc.collect(0)
-        [wr1] = self.gc.root_walker.current_stack
+        wr1 = self.gc.root_walker.pop()
         assert not wr1.wadr        # weakref to dead object
         #
+        self.gc.root_walker.push(wr1)
         self.gc.collect(0)
-        assert self.gc.root_walker.current_stack == [wr1]
+        wr1bis = self.gc.root_walker.pop()
+        assert wr1 == wr1bis
         assert not wr1.wadr
 
     def test_normalize_global_null(self):

pypy/rpython/memory/gc/test/test_stmtls.py

     pass
 
 class FakeRootWalker:
-    current_stack = ()
+    STACK_DEPTH = 200
     prebuilt_nongc = ()
 
+    def __init__(self):
+        A = lltype.Array(llmemory.Address)
+        self.current_stack = lltype.malloc(A, self.STACK_DEPTH, flavor='raw',
+                                           track_allocation=False)
+        self.stack_types = []
+
+    def push(self, ptr):
+        i = len(self.stack_types)
+        self.current_stack[i] = llmemory.cast_ptr_to_adr(ptr)
+        self.stack_types.append(lltype.typeOf(ptr))
+
+    def pop(self):
+        PTR = self.stack_types.pop()
+        i = len(self.stack_types)
+        return llmemory.cast_adr_to_ptr(self.current_stack[i], PTR)
+
     def collect_list(self, lst):
-        A = lltype.Array(llmemory.Address)
-        roots = lltype.malloc(A, len(lst), flavor='raw')
-        for i in range(len(lst)):
-            roots[i] = llmemory.cast_ptr_to_adr(lst[i])
-        for i in range(len(lst)):
-            root = lltype.direct_ptradd(lltype.direct_arrayitems(roots), i)
+        roots = lltype.direct_arrayitems(self.current_stack)
+        for i in range(len(self.stack_types)):
+            root = lltype.direct_ptradd(roots, i)
             root = llmemory.cast_ptr_to_adr(root)
             yield root
-        for i in range(len(lst)):
-            P = lltype.typeOf(lst[i])
-            lst[i] = llmemory.cast_adr_to_ptr(roots[i], P)
-        lltype.free(roots, flavor='raw')
 
     def walk_current_stack_roots(self, callback, arg):
         for root in self.collect_list(self.current_stack):
 class TestStmGCTLS(object):
 
     def setup_method(self, meth):
-        self.current_stack = []
         self.gc = FakeGC()
         self.gc.sharedarea.gc = self.gc
         self.gctls_main = StmGCTLS(self.gc)
         self.gctls_thrd = StmGCTLS(self.gc)
         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)
+        self.gc.root_walker.push(p)
 
     def stack_pop(self):
-        return self.current_stack.pop()
+        return self.gc.root_walker.pop()
 
     def malloc(self, STRUCT):
         size = llarena.round_up_for_allocation(llmemory.sizeof(STRUCT))