[pypy-commit] pypy stm-gc: id(), hash(), and small refactors

arigo noreply at buildbot.pypy.org
Mon Apr 16 18:05:05 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: stm-gc
Changeset: r54429:48a8fcb4017c
Date: 2012-04-16 17:42 +0200
http://bitbucket.org/pypy/pypy/changeset/48a8fcb4017c/

Log:	id(), hash(), and small refactors

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
@@ -319,8 +319,8 @@
             ll_assert(hdr.tid & GCFLAGS == GCFLAGS,
                       "stm_write: bogus flags on source object")
             #
-            # Remove the GCFLAG_GLOBAL from the copy
-            localhdr.tid = hdr.tid & ~GCFLAG_GLOBAL
+            # Remove the GCFLAG_GLOBAL from the copy, and add GCFLAG_VISITED
+            localhdr.tid = hdr.tid + (GCFLAG_VISITED - GCFLAG_GLOBAL)
             #
             # Set the 'version' field of the local copy to be a pointer
             # to the global obj.  (The field is called 'version' because
@@ -357,35 +357,43 @@
         """Implement the common logic of id() and identityhash()
         of an object, given as a GCREF.
         """
-        raise NotImplementedError
         obj = llmemory.cast_ptr_to_adr(gcobj)
         hdr = self.header(obj)
-        #
-        if hdr.tid & GCFLAG_GLOBAL == 0:
+        tls = self.get_tls()
+        if tls.is_in_nursery(obj):
             #
-            # The object is a local object.  Find or allocate a corresponding
-            # global object.
-            if hdr.tid & (GCFLAG_WAS_COPIED | GCFLAG_HAS_SHADOW) == 0:
+            # The object is still in the nursery of the current TLS.
+            # (It cannot be in the nursery of a different thread, because
+            # such objects are not visible to different threads at all.)
+            #
+            ll_assert(hdr.tid & GCFLAG_WAS_COPIED == 0, "id: WAS_COPIED?")
+            #
+            if hdr.tid & GCFLAG_HAS_SHADOW == 0:
                 #
                 # We need to allocate a global object here.  We only allocate
                 # it for now; it is left completely uninitialized.
+                size_gc_header = self.gcheaderbuilder.size_gc_header
                 size = self.get_size(obj)
-                tls = self.collector.get_tls()
-                globalobj = self._malloc_global_raw(tls, size)
-                self.header(globalobj).tid = GCFLAG_GLOBAL
+                totalsize = size_gc_header + size
+                fixedobj = tls.sharedarea_tls.malloc_object(totalsize)
+                tls.sharedarea_tls.add_regular(fixedobj)
+                self.header(fixedobj).tid = 0     # GCFLAG_VISITED is off
                 #
                 # Update the header of the local 'obj'
                 hdr.tid |= GCFLAG_HAS_SHADOW
-                hdr.version = globalobj
+                hdr.version = fixedobj
                 #
             else:
-                # There is already a corresponding globalobj
-                globalobj = hdr.version
+                # There is already a corresponding fixedobj
+                fixedobj = hdr.version
             #
-            obj = globalobj
+            obj = fixedobj
+            #
+        elif hdr.tid & (GCFLAG_GLOBAL|GCFLAG_WAS_COPIED) == GCFLAG_WAS_COPIED:
+            #
+            # The object is the local copy of a LOCAL-GLOBAL pair.
+            obj = hdr.version
         #
-        ll_assert(self.header(obj).tid & GCFLAG_GLOBAL != 0,
-                  "id_or_identityhash: unexpected local object")
         i = llmemory.cast_adr_to_int(obj)
         if is_hash:
             # For identityhash(), we need a special case for some
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
@@ -25,7 +25,6 @@
         self.gc = sharedarea.gc
         self.sharedarea = sharedarea
         self.chained_list = NULL
-        self.special_stack = self.gc.AddressStack()
 
     def malloc_object(self, totalsize):
         """Malloc.  You must also call add_regular() or add_special() later."""
@@ -40,10 +39,6 @@
         hdr.version = self.chained_list
         self.chained_list = obj
 
-    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
         llarena.arena_free(llarena.getfakearenaaddress(adr1))
@@ -55,10 +50,6 @@
             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
@@ -9,7 +9,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_WAS_COPIED
+from pypy.rpython.memory.gc.stmgc import GCFLAG_WAS_COPIED, GCFLAG_HAS_SHADOW
 
 
 class StmGCTLS(object):
@@ -49,12 +49,16 @@
         # --- a thread-local allocator for the shared area
         from pypy.rpython.memory.gc.stmshared import StmGCThreadLocalAllocator
         self.sharedarea_tls = StmGCThreadLocalAllocator(self.gc.sharedarea)
+        # --- the LOCAL objects with GCFLAG_WAS_COPIED
+        self.copied_local_objects = self.AddressStack()
         #
         self._register_with_C_code()
 
     def teardown_thread(self):
         self._cleanup_state()
         self._unregister_with_C_code()
+        self.copied_local_objects.delete()
+        self.sharedarea_tls.delete()
         self._free_nursery(self.nursery_start)
         free_non_gc_object(self)
 
@@ -241,7 +245,7 @@
         """Allocate an object that will be used as a LOCAL copy of
         some GLOBAL object."""
         localobj = self.sharedarea_tls.malloc_object(totalsize)
-        self.sharedarea_tls.add_special(localobj)
+        self.copied_local_objects.append(localobj)
         return localobj
 
     # ------------------------------------------------------------
@@ -264,9 +268,18 @@
         #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
+        # free the old unused local objects still allocated in the
+        # StmGCThreadLocalAllocator
         self.sharedarea_tls.clear()
+        # free the local copies.  Note that commonly, they are leftovers
+        # from the previous transaction running in this thread.  The C code
+        # has just copied them over the corresponding GLOBAL objects at the
+        # very end of that transaction.
+        self._free_and_clear_list(self.copied_local_objects)
+
+    def _free_and_clear_list(self, lst):
+        while lst.non_empty():
+            self.sharedarea_tls.free_object(lst.pop())
 
 
     def collect_roots_from_stack(self):
@@ -293,40 +306,58 @@
         obj = root.address[0]
         hdr = self.gc.header(obj)
         #
-        # If 'obj' is a LOCAL copy of a GLOBAL object, skip it
-        # (this case is handled differently in collect_roots_from_tldict)
-        if hdr.tid & GCFLAG_WAS_COPIED:
-            return
-        #
         # If 'obj' is not in the nursery, we set GCFLAG_VISITED
         if not self.is_in_nursery(obj):
             if hdr.tid & GCFLAG_VISITED == 0:
+                ll_assert(hdr.tid & GCFLAG_WAS_COPIED == 0,
+                          "local GCFLAG_WAS_COPIED without GCFLAG_VISITED")
                 hdr.tid |= GCFLAG_VISITED
                 self.pending.append(obj)
             return
         #
         # If 'obj' was already forwarded, change it to its forwarding address.
-        if hdr.tid & GCFLAG_VISITED:
-            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)
-        #
-        # Register the object here, not before the memcopy() that would
-        # overwrite its 'version' field
-        self._register_newly_malloced_obj(newobj)
+        # If 'obj' has already a shadow but isn't forwarded so far, use it.
+        if hdr.tid & (GCFLAG_VISITED | GCFLAG_HAS_SHADOW):
+            #
+            if hdr.tid & GCFLAG_VISITED:
+                root.address[0] = hdr.version
+                return
+            #
+            # Case of GCFLAG_HAS_SHADOW.  See comments below.
+            size_gc_header = self.gc.gcheaderbuilder.size_gc_header
+            size = self.gc.get_size(obj)
+            totalsize = size_gc_header + size
+            hdr.tid &= ~GCFLAG_HAS_SHADOW
+            newobj = hdr.version
+            newhdr = self.gc.header(newobj)
+            #
+            saved_version = newhdr.version
+            llmemory.raw_memcopy(obj - size_gc_header,
+                                 newobj - size_gc_header,
+                                 totalsize)
+            newhdr.version = saved_version
+            newhdr.tid = hdr.tid | GCFLAG_VISITED
+            #
+        else:
+            #
+            # 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)
+            #
+            # Register the object here, not before the memcopy() that would
+            # overwrite its 'version' field
+            self._register_newly_malloced_obj(newobj)
         #
         # Set the YOUNG copy's GCFLAG_VISITED and set its version to
         # point to the OLD copy.
@@ -366,6 +397,8 @@
                   "in a root: unexpected GCFLAG_GLOBAL")
         ll_assert(localhdr.tid & GCFLAG_WAS_COPIED != 0,
                   "in a root: missing GCFLAG_WAS_COPIED")
+        ll_assert(localhdr.tid & GCFLAG_VISITED != 0,
+                  "in a root: missing GCFLAG_VISITED")
         #
         self.trace_and_drag_out_of_nursery(localobj)
 
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
@@ -167,14 +167,21 @@
     def malloc(self, STRUCT, weakref=False, globl='auto'):
         size = llarena.round_up_for_allocation(llmemory.sizeof(STRUCT))
         tid = lltype.cast_primitive(llgroup.HALFWORD, 123)
-        gcref = self.gc.malloc_fixedsize_clear(tid, size,
-                                               contains_weakptr=weakref)
-        realobj = lltype.cast_opaque_ptr(lltype.Ptr(STRUCT), gcref)
-        addr = llmemory.cast_ptr_to_adr(realobj)
         if globl == 'auto':
             globl = (self.gc.stm_operations.threadnum == 0)
         if globl:
-            self.gc.header(addr).tid |= GCFLAG_GLOBAL
+            totalsize = self.gc.gcheaderbuilder.size_gc_header + size
+            adr1 = llarena.arena_malloc(llmemory.raw_malloc_usage(totalsize),
+                                        1)
+            llarena.arena_reserve(adr1, totalsize)
+            addr = adr1 + self.gc.gcheaderbuilder.size_gc_header
+            self.gc.header(addr).tid = GCFLAG_GLOBAL
+            realobj = llmemory.cast_adr_to_ptr(addr, lltype.Ptr(STRUCT))
+        else:
+            gcref = self.gc.malloc_fixedsize_clear(tid, size,
+                                                   contains_weakptr=weakref)
+            realobj = lltype.cast_opaque_ptr(lltype.Ptr(STRUCT), gcref)
+            addr = llmemory.cast_ptr_to_adr(realobj)
         return realobj, addr
     def select_thread(self, threadnum):
         self.gc.stm_operations.threadnum = threadnum
@@ -340,6 +347,7 @@
         assert main_tls.nursery_free == main_tls.nursery_start   # empty
 
     def test_commit_transaction_no_references(self):
+        py.test.skip("rewrite me")
         s, s_adr = self.malloc(S)
         s.b = 12345
         self.select_thread(1)
@@ -448,6 +456,7 @@
         s, s_adr = self.malloc(S)
         self.select_thread(1)
         t_adr = self.gc.stm_writebarrier(s_adr)   # make a local copy
+        assert t_adr != s_adr
         t = llmemory.cast_adr_to_ptr(t_adr, llmemory.GCREF)
         i = self.gc.id(t)
         assert i == llmemory.cast_adr_to_int(s_adr)
@@ -465,8 +474,11 @@
 
     def test_id_of_local_surviving(self):
         sr1, sr1_adr = self.malloc(SR)
+        assert sr1.s1 == lltype.nullptr(S)
+        assert sr1.sr2 == lltype.nullptr(SR)
         self.select_thread(1)
         t2, t2_adr = self.malloc(S)
+        t2.a = 423
         tr1_adr = self.gc.stm_writebarrier(sr1_adr)
         assert tr1_adr != sr1_adr
         tr1 = llmemory.cast_adr_to_ptr(tr1_adr, lltype.Ptr(SR))
@@ -478,7 +490,7 @@
         assert i == self.gc.id(t2)
         self.gc.commit_transaction()
         s2 = tr1.s1       # tr1 is a root, so not copied yet
-        assert s2 and s2 != t2
+        assert s2 and s2.a == 423 and s2._obj0 != t2._obj0
         assert self.gc.id(s2) == i
 
     def test_hash_of_global(self):
@@ -509,6 +521,7 @@
         sr1, sr1_adr = self.malloc(SR)
         self.select_thread(1)
         t2, t2_adr = self.malloc(S)
+        t2.a = 424
         tr1_adr = self.gc.stm_writebarrier(sr1_adr)
         assert tr1_adr != sr1_adr
         tr1 = llmemory.cast_adr_to_ptr(tr1_adr, lltype.Ptr(SR))
@@ -521,7 +534,7 @@
         assert i == self.gc.identityhash(t2)
         self.gc.commit_transaction()
         s2 = tr1.s1       # tr1 is a root, so not copied yet
-        assert s2 and s2 != t2
+        assert s2 and s2.a == 424 and s2._obj0 != t2._obj0
         assert self.gc.identityhash(s2) == i
 
     def test_weakref_to_global(self):


More information about the pypy-commit mailing list