[pypy-commit] pypy stm-gc: Weakref support in the GC.

arigo noreply at buildbot.pypy.org
Mon Feb 20 11:01:35 CET 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: stm-gc
Changeset: r52659:dc4d7d7854d2
Date: 2012-02-20 10:59 +0100
http://bitbucket.org/pypy/pypy/changeset/dc4d7d7854d2/

Log:	Weakref support in the GC.

diff --git a/pypy/config/translationoption.py b/pypy/config/translationoption.py
--- a/pypy/config/translationoption.py
+++ b/pypy/config/translationoption.py
@@ -75,8 +75,7 @@
                      "markcompact": [("translation.gctransformer", "framework")],
                      "minimark": [("translation.gctransformer", "framework")],
                      "stmgc": [("translation.gctransformer", "framework"),
-                               ("translation.gcrootfinder", "stm"),
-                               ("translation.rweakref", False)],  # XXX temp
+                               ("translation.gcrootfinder", "stm")],
                      },
                   cmdline="--gc"),
     ChoiceOption("gctransformer", "GC transformer that is used - internal",
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
@@ -19,6 +19,7 @@
 GCFLAG_WAS_COPIED = first_gcflag << 1     # keep in sync with et.c
 GCFLAG_HAS_SHADOW = first_gcflag << 2
 GCFLAG_FIXED_HASH = first_gcflag << 3
+GCFLAG_WEAKREF    = first_gcflag << 4
 
 
 def always_inline(fn):
@@ -48,6 +49,7 @@
                                    ('nursery_size', lltype.Signed),
                                    ('malloc_flags', lltype.Signed),
                                    ('pending_list', llmemory.Address),
+                                   ('surviving_weakrefs', llmemory.Address),
                           )
 
     TRANSLATION_PARAMS = {
@@ -160,7 +162,6 @@
                                is_finalizer_light=False,
                                contains_weakptr=False):
         #assert not needs_finalizer, "XXX" --- finalizer is just ignored
-        assert not contains_weakptr, "XXX"
         #
         # Check the mode: either in a transactional thread, or in
         # the main thread.  For now we do the same thing in both
@@ -178,6 +179,8 @@
         # Build the object.
         llarena.arena_reserve(result, totalsize)
         obj = result + size_gc_header
+        if contains_weakptr:   # check constant-folded
+            flags |= GCFLAG_WEAKREF
         self.init_gc_object(result, typeid, flags=flags)
         #
         return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
@@ -480,6 +483,9 @@
         #
         self.gc.release(self.gc.mutex_lock)
         #
+        # Fix up the weakrefs that used to point to local objects
+        self.fixup_weakrefs(tls)
+        #
         # Now, all indirectly reachable local objects have been copied into
         # the global area, and all pointers have been fixed to point to the
         # global copies, including in the local copy of the roots.  What
@@ -490,6 +496,7 @@
 
     def collect_roots_from_tldict(self, tls):
         tls.pending_list = NULL
+        tls.surviving_weakrefs = NULL
         # Enumerate the roots, which are the local copies of global objects.
         # For each root, trace it.
         CALLBACK = self.stm_operations.CALLBACK_ENUM
@@ -602,11 +609,47 @@
             # thread before the commit is really complete.
             globalhdr.version = tls.pending_list
             tls.pending_list = globalobj
+            #
+            if hdr.tid & GCFLAG_WEAKREF != 0:
+                # this was a weakref object that survives.
+                self.young_weakref_survives(tls, obj)
         #
         # Fix the original root.address[0] to point to the globalobj
         root.address[0] = globalobj
 
 
+    @dont_inline
+    def young_weakref_survives(self, tls, obj):
+        # Relink it in the tls.surviving_weakrefs chained list,
+        # via the weakpointer_offset in the local copy of the object.
+        # Do it only if the weakref points to a local object.
+        offset = self.gc.weakpointer_offset(self.gc.get_type_id(obj))
+        if self.is_in_nursery(tls, (obj + offset).address[0]):
+            (obj + offset).address[0] = tls.surviving_weakrefs
+            tls.surviving_weakrefs = obj
+
+    def fixup_weakrefs(self, tls):
+        obj = tls.surviving_weakrefs
+        while obj:
+            offset = self.gc.weakpointer_offset(self.gc.get_type_id(obj))
+            #
+            hdr = self.header(obj)
+            ll_assert(hdr.tid & GCFLAG_GLOBAL == 0,
+                      "weakref: unexpectedly global")
+            globalobj = hdr.version
+            obj2 = (globalobj + offset).address[0]
+            hdr2 = self.header(obj2)
+            ll_assert(hdr2.tid & GCFLAG_GLOBAL == 0,
+                      "weakref: points to a global")
+            if hdr2.tid & GCFLAG_WAS_COPIED:
+                obj2g = hdr2.version    # obj2 survives, going there
+            else:
+                obj2g = llmemory.NULL   # obj2 dies
+            (globalobj + offset).address[0] = obj2g
+            #
+            obj = (obj + offset).address[0]
+
+
 class _GlobalCollector(object):
     pass
 _global_collector = _GlobalCollector()
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
@@ -1,5 +1,5 @@
 import py
-from pypy.rpython.lltypesystem import lltype, llmemory, llarena, rffi
+from pypy.rpython.lltypesystem import lltype, llmemory, llarena, llgroup, rffi
 from pypy.rpython.memory.gc.stmgc import StmGC, WORD
 from pypy.rpython.memory.gc.stmgc import GCFLAG_GLOBAL, GCFLAG_WAS_COPIED
 from pypy.rpython.memory.support import mangle_hash
@@ -14,6 +14,9 @@
                                 ('sr2', lltype.Ptr(SR)),
                                 ('sr3', lltype.Ptr(SR))))
 
+WR = lltype.GcStruct('WeakRef', ('wadr', llmemory.Address))
+SWR = lltype.GcStruct('SWR', ('wr', lltype.Ptr(WR)))
+
 
 class FakeStmOperations:
     # The point of this class is to make sure about the distinction between
@@ -115,6 +118,10 @@
         ofslist = [llmemory.offsetof(SR, 's1'),
                    llmemory.offsetof(SR, 'sr2'),
                    llmemory.offsetof(SR, 'sr3')]
+    elif TYPE == WR:
+        ofslist = []
+    elif TYPE == SWR:
+        ofslist = [llmemory.offsetof(SWR, 'wr')]
     else:
         assert 0
     for ofs in ofslist:
@@ -122,6 +129,9 @@
         if addr.address[0]:
             callback(addr, arg)
 
+def fake_weakpointer_offset(tid):
+    return llmemory.offsetof(WR, 'wadr')
+
 
 class TestBasic:
     GCClass = StmGC
@@ -135,6 +145,7 @@
         self.gc.DEBUG = True
         self.gc.get_size = fake_get_size
         self.gc.trace = fake_trace
+        self.gc.weakpointer_offset = fake_weakpointer_offset
         self.gc.setup()
 
     def teardown_method(self, meth):
@@ -147,9 +158,11 @@
 
     # ----------
     # test helpers
-    def malloc(self, STRUCT):
+    def malloc(self, STRUCT, weakref=False):
         size = llarena.round_up_for_allocation(llmemory.sizeof(STRUCT))
-        gcref = self.gc.malloc_fixedsize_clear(123, size)
+        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)
         return realobj, addr
@@ -485,3 +498,53 @@
         s2 = tr1.s1       # tr1 is a root, so not copied yet
         assert s2 and s2 != t2
         assert self.gc.identityhash(s2) == i
+
+    def test_weakref_to_global(self):
+        swr1, swr1_adr = self.malloc(SWR)
+        s2, s2_adr = self.malloc(S)
+        self.select_thread(1)
+        wr1, wr1_adr = self.malloc(WR, weakref=True)
+        wr1.wadr = s2_adr
+        twr1_adr = self.gc.stm_writebarrier(swr1_adr)
+        twr1 = llmemory.cast_adr_to_ptr(twr1_adr, lltype.Ptr(SWR))
+        twr1.wr = wr1
+        self.gc.commit_transaction()
+        wr2 = twr1.wr      # twr1 is a root, so not copied yet
+        assert wr2 and wr2 != wr1
+        assert wr2.wadr == s2_adr   # survives
+
+    def test_weakref_to_local_dying(self):
+        swr1, swr1_adr = self.malloc(SWR)
+        self.select_thread(1)
+        t2, t2_adr = self.malloc(S)
+        wr1, wr1_adr = self.malloc(WR, weakref=True)
+        wr1.wadr = t2_adr
+        twr1_adr = self.gc.stm_writebarrier(swr1_adr)
+        twr1 = llmemory.cast_adr_to_ptr(twr1_adr, lltype.Ptr(SWR))
+        twr1.wr = wr1
+        self.gc.commit_transaction()
+        wr2 = twr1.wr      # twr1 is a root, so not copied yet
+        assert wr2 and wr2 != wr1
+        assert wr2.wadr == llmemory.NULL   # dies
+
+    def test_weakref_to_local_surviving(self):
+        sr1, sr1_adr = self.malloc(SR)
+        swr1, swr1_adr = self.malloc(SWR)
+        self.select_thread(1)
+        t2, t2_adr = self.malloc(S)
+        wr1, wr1_adr = self.malloc(WR, weakref=True)
+        wr1.wadr = t2_adr
+        twr1_adr = self.gc.stm_writebarrier(swr1_adr)
+        twr1 = llmemory.cast_adr_to_ptr(twr1_adr, lltype.Ptr(SWR))
+        twr1.wr = wr1
+        tr1_adr = self.gc.stm_writebarrier(sr1_adr)
+        tr1 = llmemory.cast_adr_to_ptr(tr1_adr, lltype.Ptr(SR))
+        tr1.s1 = t2
+        t2.a = 4242
+        self.gc.commit_transaction()
+        wr2 = twr1.wr      # twr1 is a root, so not copied yet
+        assert wr2 and wr2 != wr1
+        assert wr2.wadr and wr2.wadr != t2_adr       # survives
+        s2 = llmemory.cast_adr_to_ptr(wr2.wadr, lltype.Ptr(S))
+        assert s2.a == 4242
+        assert s2 == tr1.s1   # tr1 is a root, so not copied yet


More information about the pypy-commit mailing list