[pypy-commit] pypy stm-gc: Complete the tests and the code.

arigo noreply at buildbot.pypy.org
Mon Apr 23 21:11:09 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: stm-gc
Changeset: r54692:3e6d963c1ab2
Date: 2012-04-23 15:16 +0200
http://bitbucket.org/pypy/pypy/changeset/3e6d963c1ab2/

Log:	Complete the tests and the code.

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
@@ -47,6 +47,10 @@
         # --- the LOCAL objects which are weakrefs.  They are also listed
         #     in the appropriate place, like sharedarea_tls, if needed.
         self.local_weakrefs = self.AddressStack()
+        # --- main thread only: this is the list of GLOBAL objects that
+        #     have been turned into LOCAL objects
+        if in_main_thread:
+            self.main_thread_was_global_objects = NULL
         #
         self._register_with_C_code()
 
@@ -105,6 +109,16 @@
         """Called on the main thread, just before spawning the other
         threads."""
         self.stop_transaction()
+        #
+        # We must also mark the following objects as GLOBAL again
+        obj = self.main_thread_was_global_objects
+        self.main_thread_was_global_objects = NULL
+        while obj:
+            hdr = self.gc.header(obj)
+            hdr.tid |= GCFLAG_GLOBAL
+            obj = hdr.version
+        if not we_are_translated():
+            del self.main_thread_was_global_objects   # don't use any more
 
     def leave_transactional_mode(self):
         """Restart using the main thread for mallocs."""
@@ -113,11 +127,16 @@
                 if value is not self:
                     del StmGCTLS.nontranslated_dict[key]
         self.start_transaction()
-        # do something special here after we restarted the "transaction"
-        # in the main thread: we have to make sure that the write_barrier
-        # calls done in the main thread before enter/leave_transactional_mode
-        # are still pointing to local objects.  Conservatively, we mark as
-        # local all objects directly referenced from the stack.
+        #
+        # Do something special here after we restarted the "transaction"
+        # in the main thread.  At this point, *all* objects are GLOBAL.
+        # The write_barrier will ensure that any write makes the written-to
+        # objects LOCAL again.  However, it is possible that the write
+        # barrier was called before the enter/leave_transactional_mode()
+        # and will not be called again before writing.  But such objects
+        # are right now directly in the stack.  So to fix this issue, we
+        # conservatively mark as local all objects directly from the stack.
+        self.main_thread_was_global_objects = NULL
         self.gc.root_walker.walk_current_stack_roots(
             StmGCTLS._remark_object_as_local, self)
 
@@ -195,6 +214,8 @@
         # objects.
         if not self.in_main_thread:
             self.collect_roots_from_tldict()
+        else:
+            self.collect_from_main_thread_was_global_objects()
         #
         # Now repeatedly follow objects until 'pending' is empty.
         self.collect_flush_pending()
@@ -264,14 +285,19 @@
         self.local_weakrefs.append(obj)
 
     def _remark_object_as_local(self, root):
-        self.main_thread_writes_to_global_obj(root.address[0])
+        obj = root.address[0]
+        hdr = self.gc.header(obj)
+        if hdr.tid & GCFLAG_GLOBAL:
+            self.main_thread_writes_to_global_obj(obj)
 
     def main_thread_writes_to_global_obj(self, obj):
         hdr = self.gc.header(obj)
         ll_assert(hdr.tid & GCFLAG_WAS_COPIED == 0,
                   "write in main thread: unexpected GCFLAG_WAS_COPIED")
         hdr.tid &= ~GCFLAG_GLOBAL
-        self.sharedarea_tls.add_regular(obj)
+        # add the object into this linked list
+        hdr.version = self.main_thread_was_global_objects
+        self.main_thread_was_global_objects = obj
 
     # ------------------------------------------------------------
 
@@ -474,6 +500,19 @@
         #
         self.trace_and_drag_out_of_nursery(localobj)
 
+    def collect_from_main_thread_was_global_objects(self):
+        # NB. all objects in the 'main_thread_was_global_objects' list are
+        # currently immortal (because they were once GLOBAL)
+        obj = self.main_thread_was_global_objects
+        while obj:
+            hdr = self.gc.header(obj)
+            ll_assert(hdr.tid & GCFLAG_GLOBAL == 0,
+                      "unexpected GLOBAL in main_thread_was_global_objects")
+            if hdr.tid & GCFLAG_VISITED == 0:
+                hdr.tid |= GCFLAG_VISITED
+                self.pending.append(obj)
+            obj = hdr.version
+
     def collect_flush_pending(self):
         # Follow the objects in the 'pending' stack and move the
         # young objects they point to out of the nursery.
diff --git a/pypy/rpython/memory/gc/test/test_stmgc.py b/pypy/rpython/memory/gc/test/test_stmgc.py
--- a/pypy/rpython/memory/gc/test/test_stmgc.py
+++ b/pypy/rpython/memory/gc/test/test_stmgc.py
@@ -342,23 +342,56 @@
         from pypy.rpython.memory.gc.test import test_stmtls
         self.gc.root_walker = test_stmtls.FakeRootWalker()
         #
-        tr1, tr1_adr = self.malloc(SR, globl=True)
+        tr1, tr1_adr = self.malloc(SR, globl=True)   # three prebuilt 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]
         obj = self.gc.stm_writebarrier(tr1_adr)
         assert obj == tr1_adr
         obj = self.gc.stm_writebarrier(tr2_adr)
         assert obj == tr2_adr
+        obj = self.gc.stm_writebarrier(tr3_adr)
+        assert obj == tr3_adr
         self.checkflags(tr1_adr, False, False)    # tr1 has become local
         self.checkflags(tr2_adr, False, False)    # tr2 has become local
+        self.checkflags(tr3_adr, False, False)    # tr3 has become local
         #
         self.gc.enter_transactional_mode()
         self.gc.leave_transactional_mode()
         self.checkflags(tr2_adr, True, False)     # tr2 has become global again
+        self.checkflags(tr3_adr, True, False)     # tr3 has become global again
         # but tr1 is still local, because it is directly referenced from stack
         self.checkflags(tr1_adr, False, False)
 
+    def test_non_prebuilt_relocalize_after_transactional_mode(self):
+        from pypy.rpython.memory.gc.test import test_stmtls
+        self.gc.root_walker = test_stmtls.FakeRootWalker()
+        #
+        tr1, tr1_adr = self.malloc(SR)            # local
+        tr2, tr2_adr = self.malloc(SR)            # local
+        tr1.sr2 = tr2
+        self.gc.root_walker.current_stack = [tr1]
+        self.gc.enter_transactional_mode()
+        self.checkflags(tr1_adr, True, False)     # tr1 has become global
+        self.checkflags(tr2_adr, True, False)     # tr2 has become global
+        self.gc.leave_transactional_mode()
+        self.checkflags(tr2_adr, True, False)     # tr2 is still global
+        self.checkflags(tr1_adr, False, False)    # tr1 is back to a local
+
+    def test_collect_from_main_thread_was_global_objects(self):
+        tr1, tr1_adr = self.malloc(SR, globl=True)  # a global prebuilt object
+        tr2, tr2_adr = self.malloc(SR, globl=False) # tr2 is a local
+        self.checkflags(tr2_adr, False, False)      # check that tr2 is a local
+        obj = self.gc.stm_writebarrier(tr1_adr)
+        assert obj == tr1_adr                       # tr1 is now local
+        tr1.sr2 = tr2
+        self.gc.collect(0)
+        tr2 = tr1.sr2       # reload, because it moved
+        tr2_adr = llmemory.cast_ptr_to_adr(tr2)
+        self.checkflags(tr2_adr, False, False)  # tr2 is still alive and local
+        self.checkflags(tr1_adr, False, False)  # tr1 is still local
+
     def test_commit_transaction_empty(self):
         self.select_thread(1)
         s, s_adr = self.malloc(S)


More information about the pypy-commit mailing list