Author: Armin Rigo <ar...@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 _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit