[pypy-commit] pypy stm-gc: Start to write support code around the GC for getting at thread-local

arigo noreply at buildbot.pypy.org
Sun Apr 29 09:24:07 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: stm-gc
Changeset: r54797:133049a5ba84
Date: 2012-04-29 09:21 +0200
http://bitbucket.org/pypy/pypy/changeset/133049a5ba84/

Log:	Start to write support code around the GC for getting at thread-
	local data. This, and (by the way) also regular GC pointers in non-
	GC prebuilt structs, are not supported yet by the GC; it comes next.

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
@@ -86,14 +86,17 @@
 #     code (see tldict_lookup()).
 #
 # Invariant: between two transactions, all objects visible from the current
-# thread are always GLOBAL.  In particular:
+# thread are always GLOBAL, with the exception of the ones only reachable
+# via thread-local data structures, which remain LOCAL.  In particular:
 #
-#   - The LOCAL object of a thread are not visible at all from other threads.
+#   - The LOCAL objects of a thread are not visible at all from other threads.
 #     This means that in transactional mode there is *no* pointer from a
 #     GLOBAL object directly to a LOCAL object.
 #
 #   - At the end of enter_transactional_mode(), and at the beginning of
 #     leave_transactional_mode(), *all* objects everywhere are GLOBAL.
+#     (With the possible exception of the thread-local ones from the main
+#     thread.)
 #
 # Collection: for now we have only local_collection(), which ignores all
 # GLOBAL objects.
@@ -110,9 +113,11 @@
 #
 #   - A special case is the end-of-transaction collection, done by the same
 #     local_collection() with a twist: all pointers to a LOCAL COPY object
-#     are replaced with copies to the corresponding GLOBAL original.  When
-#     it is done, we mark all surviving LOCAL objects as GLOBAL too, and we
-#     are back to the situation where this thread sees only GLOBAL objects.
+#     are replaced with copies to the corresponding GLOBAL original.
+#     We additionally mark all surviving LOCAL objects as GLOBAL,
+#     unless they are only reachable via thread-locals.
+#     Then we are back to the situation where this thread sees only
+#     GLOBAL objects (again, with the exception of thread-locals).
 #     What we leave to the C code to do "as a finishing touch" is to copy
 #     transactionally the content of the LOCAL COPY objects back over the
 #     GLOBAL originals; before this is done, the transaction can be aborted
@@ -127,7 +132,7 @@
 #   - if GCFLAG_WAS_COPIED, it points to the GLOBAL original.
 #
 #   - if GCFLAG_HAS_SHADOW, to the shadow object outside the nursery.
-#     (It is not used on any other nursery object.)
+#     (It is not used on other nursery objects before collection.)
 #
 #   - it contains the 'next' object of the 'mt_global_turned_local' list.
 #
@@ -163,6 +168,7 @@
     #needs_write_barrier = "stm"
     prebuilt_gc_objects_are_static_roots = False
     malloc_zero_filled = True    # xxx?
+    handles_thread_locals = True
 
     HDR = lltype.Struct('header', ('tid', lltype.Signed),
                                   ('version', llmemory.Address))
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
@@ -168,6 +168,7 @@
         a_random_address = llmemory.cast_ptr_to_adr(foo)
         gcdata.static_root_start = a_random_address      # patched in finish()
         gcdata.static_root_nongcend = a_random_address   # patched in finish()
+        gcdata.static_root_thrlocend = a_random_address  # patched in finish()
         gcdata.static_root_end = a_random_address        # patched in finish()
         gcdata.max_type_id = 13                          # patched in finish()
         gcdata.typeids_z = a_random_address              # patched in finish()
@@ -207,6 +208,9 @@
             'static_root_nongcend',
             annmodel.SomeAddress())
         data_classdef.generalize_attr(
+            'static_root_thrlocend',
+            annmodel.SomeAddress())
+        data_classdef.generalize_attr(
             'static_root_end',
             annmodel.SomeAddress())
         data_classdef.generalize_attr(
@@ -583,9 +587,11 @@
         r_gcdata = self.translator.rtyper.getrepr(s_gcdata)
         ll_instance = rmodel.inputconst(r_gcdata, self.gcdata).value
 
+        lb = self.layoutbuilder
         addresses_of_static_ptrs = (
-            self.layoutbuilder.addresses_of_static_ptrs_in_nongc +
-            self.layoutbuilder.addresses_of_static_ptrs)
+            lb.addresses_of_static_ptrs_in_nongc +
+            lb.addresses_of_static_getters_thrloc +
+            lb.addresses_of_static_ptrs)
         log.info("found %s static roots" % (len(addresses_of_static_ptrs), ))
         ll_static_roots_inside = lltype.malloc(lltype.Array(llmemory.Address),
                                                len(addresses_of_static_ptrs),
@@ -593,9 +599,23 @@
 
         for i in range(len(addresses_of_static_ptrs)):
             ll_static_roots_inside[i] = addresses_of_static_ptrs[i]
-        ll_instance.inst_static_root_start = llmemory.cast_ptr_to_adr(ll_static_roots_inside) + llmemory.ArrayItemsOffset(lltype.Array(llmemory.Address))
-        ll_instance.inst_static_root_nongcend = ll_instance.inst_static_root_start + llmemory.sizeof(llmemory.Address) * len(self.layoutbuilder.addresses_of_static_ptrs_in_nongc)
-        ll_instance.inst_static_root_end = ll_instance.inst_static_root_start + llmemory.sizeof(llmemory.Address) * len(addresses_of_static_ptrs)
+
+        ll_instance.inst_static_root_start = (
+            llmemory.cast_ptr_to_adr(ll_static_roots_inside) +
+            llmemory.ArrayItemsOffset(lltype.Array(llmemory.Address)))
+        ll_instance.inst_static_root_nongcend = (
+            ll_instance.inst_static_root_start +
+            sizeofaddr * len(lb.addresses_of_static_ptrs_in_nongc))
+        ll_instance.inst_static_root_thrlocend = (
+            ll_instance.inst_static_root_nongcend +
+            sizeofaddr * len(lb.addresses_of_static_getters_thrloc))
+        ll_instance.inst_static_root_end = (
+            ll_instance.inst_static_root_thrlocend +
+            sizeofaddr * len(lb.addresses_of_static_ptrs))
+        assert ll_instance.inst_static_root_end == (
+            ll_instance.inst_static_root_start +
+            sizeofaddr * len(addresses_of_static_ptrs))
+
         newgcdependencies = []
         newgcdependencies.append(ll_static_roots_inside)
         ll_instance.inst_max_type_id = len(group.members)
@@ -607,6 +627,15 @@
             ll_typeids_z[i] = typeids_z[i]
         ll_instance.inst_typeids_z = llmemory.cast_ptr_to_adr(ll_typeids_z)
         newgcdependencies.append(ll_typeids_z)
+        #
+        if lb.addresses_of_static_getters_thrloc:
+            annhelper = annlowlevel.MixLevelHelperAnnotator(
+                self.translator.rtyper)
+            for getter in lb.addresses_of_static_getters_thrloc:
+                annhelper.getgraph(getter.ptr._obj._callable, [],
+                                   annmodel.SomeAddress())
+            annhelper.finish()
+        #
         return newgcdependencies
 
     def get_finish_tables(self):
@@ -1374,7 +1403,8 @@
 
     def walk_roots(self, collect_stack_root,
                    collect_static_in_prebuilt_nongc,
-                   collect_static_in_prebuilt_gc):
+                   collect_static_in_prebuilt_gc,
+                   collect_static_in_prebuilt_threadlocal=None):
         gcdata = self.gcdata
         gc = self.gc
         if collect_static_in_prebuilt_nongc:
@@ -1385,8 +1415,18 @@
                 if gc.points_to_valid_gc_object(result):
                     collect_static_in_prebuilt_nongc(gc, result)
                 addr += sizeofaddr
+        if collect_static_in_prebuilt_threadlocal:
+            addr = gcdata.static_root_nongcend
+            end = gcdata.static_root_thrlocend
+            while addr != end:
+                fadr = addr.address[0]
+                fptr = llmemory.cast_adr_to_ptr(fadr, gctypelayout.GETTERFN)
+                result = fptr()
+                if gc.points_to_valid_gc_object(result):
+                    collect_static_in_prebuilt_threadlocal(gc, result)
+                addr += sizeofaddr
         if collect_static_in_prebuilt_gc:
-            addr = gcdata.static_root_nongcend
+            addr = gcdata.static_root_thrlocend
             end = gcdata.static_root_end
             while addr != end:
                 result = addr.address[0]
diff --git a/pypy/rpython/memory/gctypelayout.py b/pypy/rpython/memory/gctypelayout.py
--- a/pypy/rpython/memory/gctypelayout.py
+++ b/pypy/rpython/memory/gctypelayout.py
@@ -267,9 +267,14 @@
         # prebuilt structures.  It should list all the locations that could
         # possibly point to a GC heap object.
         # this lists contains pointers in GcStructs and GcArrays
+        # (note that on some GCs, we don't need this, so it is empty)
         self.addresses_of_static_ptrs = []
         # this lists contains pointers in raw Structs and Arrays
         self.addresses_of_static_ptrs_in_nongc = []
+        # this lists contains addresses of functions that just return
+        # the thread-local address of a GC pointer from a raw Struct
+        # with the 'stm_thread_local' hint.
+        self.addresses_of_static_getters_thrloc = []
         # for debugging, the following list collects all the prebuilt
         # GcStructs and GcArrays
         self.all_prebuilt_gc = []
@@ -425,24 +430,28 @@
         adr = llmemory.cast_ptr_to_adr(value._as_ptr())
         if TYPE._gckind == "gc":
             if gc.prebuilt_gc_objects_are_static_roots or gc.DEBUG:
-                appendto = self.addresses_of_static_ptrs
+                append = self.addresses_of_static_ptrs.append
             else:
                 return
         elif hasattr(TYPE, "_hints") and TYPE._hints.get('stm_thread_local'):
-            # The exception data's value object is skipped: it's a thread-
-            # local data structure.  We assume that objects are stored
-            # only temporarily there, so it is always cleared at the point
-            # where we collect the roots.
-            if TYPE._name == 'ExcData':
-                return
-            # Some other thread-local data don't have any GC pointers
-            # themselves.  These are fine.
-            assert list(gc_pointers_inside(value, adr)) == []
-            return
+            assert gc.handles_thread_locals
+            def append(a):
+                def getter():
+                    return a
+                from pypy.annotation import model as annmodel
+                from pypy.rpython.annlowlevel import MixLevelHelperAnnotator
+                annhelper = MixLevelHelperAnnotator(self.translator.rtyper)
+                fptr = annhelper.delayedfunction(getter, [],
+                                                 annmodel.SomeAddress())
+                annhelper.finish()
+                adr = llmemory.cast_ptr_to_adr(fptr)
+                self.addresses_of_static_getters_thrloc.append(adr)
         else:
-            appendto = self.addresses_of_static_ptrs_in_nongc
+            append = self.addresses_of_static_ptrs_in_nongc.append
         for a in gc_pointers_inside(value, adr, mutable_only=True):
-            appendto.append(a)
+            append(a)
+
+GETTERFN = lltype.Ptr(lltype.FuncType([], llmemory.Address))
 
 # ____________________________________________________________
 #


More information about the pypy-commit mailing list