Author: Armin Rigo <[email protected]>
Branch: stm-gc
Changeset: r52033:28f4cfffe4db
Date: 2012-02-02 16:46 +0100
http://bitbucket.org/pypy/pypy/changeset/28f4cfffe4db/
Log: Read and write barriers.
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
@@ -3,6 +3,7 @@
from pypy.rpython.lltypesystem.llmemory import raw_malloc_usage
from pypy.rpython.memory.gc.base import GCBase
from pypy.rlib.rarithmetic import LONG_BIT
+from pypy.rlib.debug import ll_assert
WORD = LONG_BIT // 8
@@ -53,7 +54,9 @@
GCBase.__init__(self, config, **kwds)
self.stm_operations = stm_operations
self.max_nursery_size = max_nursery_size
-
+ #
+ self.declare_readers()
+ self.declare_write_barrier()
def setup(self):
"""Called at run-time to initialize the GC."""
@@ -71,7 +74,7 @@
def setup_thread(self, in_main_thread):
tls = lltype.malloc(self.GCTLS, flavor='raw')
- self.stm_operations.set_tls(llmemory.cast_ptr_to_adr(tls))
+ self.stm_operations.set_tls(self, llmemory.cast_ptr_to_adr(tls))
tls.nursery_start = self._alloc_nursery()
tls.nursery_size = self.max_nursery_size
tls.nursery_free = tls.nursery_start
@@ -89,7 +92,7 @@
def teardown_thread(self):
tls = self.get_tls()
- self.stm_operations.set_tls(NULL)
+ self.stm_operations.set_tls(self, NULL)
self._free_nursery(tls.nursery_start)
lltype.free(tls, flavor='raw')
@@ -142,6 +145,17 @@
return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
+ def _malloc_local_raw(self, size):
+ # for _stm_write_barrier_global(): a version of malloc that does
+ # no initialization of the malloc'ed object
+ size_gc_header = self.gcheaderbuilder.size_gc_header
+ totalsize = size_gc_header + size
+ result = self.allocate_bump_pointer(totalsize)
+ llarena.arena_reserve(result, totalsize)
+ obj = result + size_gc_header
+ return obj
+
+
@always_inline
def combine(self, typeid16, flags):
return llop.combine_ushort(lltype.Signed, typeid16, flags)
@@ -150,3 +164,95 @@
def init_gc_object(self, addr, typeid16, flags=0):
hdr = llmemory.cast_adr_to_ptr(addr, lltype.Ptr(self.HDR))
hdr.tid = self.combine(typeid16, flags)
+
+ # ----------
+
+ def declare_readers(self):
+ # Reading functions. Defined here to avoid the extra burden of
+ # passing 'self' explicitly.
+ stm_operations = self.stm_operations
+ #
+ @always_inline
+ def read_signed(obj, offset):
+ if self.header(obj).tid & GCFLAG_GLOBAL == 0:
+ return (obj + offset).signed[0] # local obj: read directly
+ else:
+ return _read_word_global(obj, offset) # else: call a helper
+ self.read_signed = read_signed
+ #
+ @dont_inline
+ def _read_word_global(obj, offset):
+ hdr = self.header(obj)
+ if hdr.tid & GCFLAG_WAS_COPIED != 0:
+ #
+ # Look up in the thread-local dictionary.
+ localobj = stm_operations.tldict_lookup(obj)
+ if localobj:
+ ll_assert(self.header(localobj).tid & GCFLAG_GLOBAL == 0,
+ "stm_read: tldict_lookup() -> GLOBAL obj")
+ return (localobj + offset).signed[0]
+ #
+ return stm_operations.stm_read_word(obj, offset)
+
+
+ def declare_write_barrier(self):
+ # Write barrier. Defined here to avoid the extra burden of
+ # passing 'self' explicitly.
+ stm_operations = self.stm_operations
+ #
+ @always_inline
+ def write_barrier(obj):
+ if self.header(obj).tid & GCFLAG_GLOBAL != 0:
+ obj = _stm_write_barrier_global(obj)
+ return obj
+ self.write_barrier = write_barrier
+ #
+ @dont_inline
+ def _stm_write_barrier_global(obj):
+ # we need to find of make a local copy
+ hdr = self.header(obj)
+ if hdr.tid & GCFLAG_WAS_COPIED == 0:
+ # in this case, we are sure that we don't have a copy
+ hdr.tid |= GCFLAG_WAS_COPIED
+ # ^^^ non-protected write, but concurrent writes should
+ # have the same effect, so fine
+ else:
+ # in this case, we need to check first
+ localobj = stm_operations.tldict_lookup(obj)
+ if localobj:
+ hdr = self.header(localobj)
+ ll_assert(hdr.tid & GCFLAG_GLOBAL == 0,
+ "stm_write: tldict_lookup() -> GLOBAL obj")
+ ll_assert(hdr.tid & GCFLAG_WAS_COPIED != 0,
+ "stm_write: tldict_lookup() -> non-COPIED obj")
+ return localobj
+ #
+ # Here, we need to really make a local copy
+ size = self.get_size(obj)
+ try:
+ localobj = self._malloc_local_raw(size)
+ except MemoryError:
+ # XXX
+ fatalerror("MemoryError in _stm_write_barrier_global -- sorry")
+ return llmemory.NULL
+ #
+ # Initialize the copy by doing an stm raw copy of the bytes
+ stm_operations.stm_copy_transactional_to_raw(obj, localobj, size)
+ #
+ # The raw copy done above includes all header fields.
+ # Check at least the gc flags of the copy.
+ hdr = self.header(obj)
+ localhdr = self.header(localobj)
+ GCFLAGS = (GCFLAG_GLOBAL | GCFLAG_WAS_COPIED)
+ ll_assert(hdr.tid & GCFLAGS == GCFLAGS,
+ "stm_write: bogus flags on source object")
+ ll_assert(localhdr.tid & GCFLAGS == GCFLAGS,
+ "stm_write: flags not copied!")
+ #
+ # Remove the GCFLAG_GLOBAL from the copy
+ localhdr.tid &= ~GCFLAG_GLOBAL
+ #
+ # Register the object as a valid copy
+ stm_operations.tldict_add(obj, localobj)
+ #
+ return 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
@@ -1,15 +1,71 @@
from pypy.rpython.lltypesystem import lltype, llmemory
from pypy.rpython.memory.gc.stmgc import StmGC
-from pypy.rpython.memory.gc.stmgc import GCFLAG_GLOBAL
+from pypy.rpython.memory.gc.stmgc import GCFLAG_GLOBAL, GCFLAG_WAS_COPIED
+
+
+S = lltype.GcStruct('S', ('a', lltype.Signed), ('b', lltype.Signed))
+ofs_a = llmemory.offsetof(S, 'a')
class FakeStmOperations:
- def set_tls(self, tls):
+ threadnum = 0 # 0 = main thread; 1,2,3... = transactional threads
+
+ def set_tls(self, gc, tls):
assert lltype.typeOf(tls) == llmemory.Address
- self._tls = tls
+ if self.threadnum == 0:
+ assert not hasattr(self, '_tls_dict')
+ assert not hasattr(self, '_gc')
+ self._tls_dict = {0: tls}
+ self._tldicts = {0: {}}
+ self._gc = gc
+ self._transactional_copies = []
+ else:
+ assert self._gc is gc
+ self._tls_dict[self.threadnum] = tls
+ self._tldicts[self.threadnum] = {}
def get_tls(self):
- return self._tls
+ return self._tls_dict[self.threadnum]
+
+ def tldict_lookup(self, obj):
+ assert lltype.typeOf(obj) == llmemory.Address
+ assert obj
+ tldict = self._tldicts[self.threadnum]
+ return tldict.get(obj, llmemory.NULL)
+
+ def tldict_add(self, obj, localobj):
+ assert lltype.typeOf(obj) == llmemory.Address
+ tldict = self._tldicts[self.threadnum]
+ assert obj not in tldict
+ tldict[obj] = localobj
+
+ class stm_read_word:
+ def __init__(self, obj, offset):
+ self.obj = obj
+ self.offset = offset
+ def __repr__(self):
+ return 'stm_read_word(%r, %r)' % (self.obj, self.offset)
+ def __eq__(self, other):
+ return (type(self) is type(other) and
+ self.__dict__ == other.__dict__)
+ def __ne__(self, other):
+ return not (self == other)
+
+ def stm_copy_transactional_to_raw(self, srcobj, dstobj, size):
+ sizehdr = self._gc.gcheaderbuilder.size_gc_header
+ srchdr = srcobj - sizehdr
+ dsthdr = dstobj - sizehdr
+ llmemory.raw_memcopy(srchdr, dsthdr, sizehdr)
+ llmemory.raw_memcopy(srcobj, dstobj, size)
+ self._transactional_copies.append((srcobj, dstobj))
+
+
+def fake_get_size(obj):
+ TYPE = obj.ptr._TYPE.TO
+ if isinstance(TYPE, lltype.GcStruct):
+ return llmemory.sizeof(TYPE)
+ else:
+ assert 0
class TestBasic:
@@ -21,8 +77,27 @@
self.gc = self.GCClass(config, FakeStmOperations(),
translated_to_c=False)
self.gc.DEBUG = True
+ self.gc.get_size = fake_get_size
self.gc.setup()
+ def teardown_method(self, meth):
+ for key in self.gc.stm_operations._tls_dict.keys():
+ if key != 0:
+ self.gc.stm_operations.threadnum = key
+ self.gc.teardown_thread()
+
+ # ----------
+ # test helpers
+ def malloc(self, STRUCT):
+ gcref = self.gc.malloc_fixedsize_clear(123, llmemory.sizeof(STRUCT))
+ 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
+ if threadnum not in self.gc.stm_operations._tls_dict:
+ self.gc.setup_thread(False)
+
def test_gc_creation_works(self):
pass
@@ -36,7 +111,6 @@
assert a6 - a5 == 5
def test_malloc_fixedsize_clear(self):
- S = lltype.GcStruct('S', ('a', lltype.Signed), ('b', lltype.Signed))
gcref = self.gc.malloc_fixedsize_clear(123, llmemory.sizeof(S))
s = lltype.cast_opaque_ptr(lltype.Ptr(S), gcref)
assert s.a == 0
@@ -45,13 +119,77 @@
assert gcref2 != gcref
def test_malloc_main_vs_thread(self):
- S = lltype.GcStruct('S', ('a', lltype.Signed), ('b', lltype.Signed))
gcref = self.gc.malloc_fixedsize_clear(123, llmemory.sizeof(S))
obj = llmemory.cast_ptr_to_adr(gcref)
- assert (self.gc.header(obj).tid & GCFLAG_GLOBAL) != 0
+ assert self.gc.header(obj).tid & GCFLAG_GLOBAL != 0
#
- self.gc.setup_thread(False)
+ self.select_thread(1)
gcref = self.gc.malloc_fixedsize_clear(123, llmemory.sizeof(S))
obj = llmemory.cast_ptr_to_adr(gcref)
- assert (self.gc.header(obj).tid & GCFLAG_GLOBAL) == 0
- self.gc.teardown_thread()
+ assert self.gc.header(obj).tid & GCFLAG_GLOBAL == 0
+
+ def test_reader_direct(self):
+ s, s_adr = self.malloc(S)
+ assert self.gc.header(s_adr).tid & GCFLAG_GLOBAL != 0
+ s.a = 42
+ value = self.gc.read_signed(s_adr, ofs_a)
+ assert value == FakeStmOperations.stm_read_word(s_adr, ofs_a)
+ #
+ self.select_thread(1)
+ s, s_adr = self.malloc(S)
+ assert self.gc.header(s_adr).tid & GCFLAG_GLOBAL == 0
+ self.gc.header(s_adr).tid |= GCFLAG_WAS_COPIED # should be ignored
+ s.a = 42
+ value = self.gc.read_signed(s_adr, ofs_a)
+ assert value == 42
+
+ def test_reader_through_dict(self):
+ s, s_adr = self.malloc(S)
+ s.a = 42
+ #
+ self.select_thread(1)
+ t, t_adr = self.malloc(S)
+ t.a = 84
+ #
+ self.gc.header(s_adr).tid |= GCFLAG_WAS_COPIED
+ self.gc.stm_operations._tldicts[1][s_adr] = t_adr
+ #
+ value = self.gc.read_signed(s_adr, ofs_a)
+ assert value == 84
+
+ def test_write_barrier_exists(self):
+ self.select_thread(1)
+ t, t_adr = self.malloc(S)
+ obj = self.gc.write_barrier(t_adr) # local object
+ assert obj == t_adr
+ #
+ self.select_thread(0)
+ s, s_adr = self.malloc(S)
+ #
+ self.select_thread(1)
+ self.gc.header(s_adr).tid |= GCFLAG_WAS_COPIED
+ self.gc.header(t_adr).tid |= GCFLAG_WAS_COPIED
+ self.gc.stm_operations._tldicts[1][s_adr] = t_adr
+ obj = self.gc.write_barrier(s_adr) # global copied object
+ assert obj == t_adr
+ assert self.gc.stm_operations._transactional_copies == []
+
+ def test_write_barrier_new(self):
+ self.select_thread(0)
+ s, s_adr = self.malloc(S)
+ s.a = 12
+ s.b = 34
+ #
+ self.select_thread(1)
+ t_adr = self.gc.write_barrier(s_adr) # global object, not copied so far
+ assert t_adr != s_adr
+ t = t_adr.ptr
+ assert t.a == 12
+ assert t.b == 34
+ assert self.gc.stm_operations._transactional_copies == [(s_adr, t_adr)]
+ #
+ u_adr = self.gc.write_barrier(s_adr) # again
+ assert u_adr == t_adr
+ #
+ u_adr = self.gc.write_barrier(u_adr) # local object
+ assert u_adr == t_adr
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit