Author: Armin Rigo <[email protected]>
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
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit