Author: Armin Rigo <ar...@tunes.org> Branch: stm-thread-2 Changeset: r57203:f23da2c9b5f4 Date: 2012-09-07 10:01 +0200 http://bitbucket.org/pypy/pypy/changeset/f23da2c9b5f4/
Log: Fix 'stmgc': in-progress diff --git a/pypy/annotation/model.py b/pypy/annotation/model.py --- a/pypy/annotation/model.py +++ b/pypy/annotation/model.py @@ -203,7 +203,6 @@ class SomeInteger(SomeFloat): "Stands for an object which is known to be an integer." knowntype = int - # size is in multiples of C's sizeof(long)! def __init__(self, nonneg=False, unsigned=None, knowntype=None): assert (knowntype is None or knowntype is int or issubclass(knowntype, base_int)) diff --git a/pypy/rpython/lltypesystem/llmemory.py b/pypy/rpython/lltypesystem/llmemory.py --- a/pypy/rpython/lltypesystem/llmemory.py +++ b/pypy/rpython/lltypesystem/llmemory.py @@ -515,6 +515,13 @@ else: return 0 + def _cast_to_uint(self, symbolic=False): + if self: + assert not isinstance(self.ptr._obj0, int) + return AddressAsUInt(self) + else: + return r_uint(0) + def _fixup(self): if self.ptr is not None and self.ptr._was_freed(): # hack to support llarena.test_replace_object_with_stub() @@ -549,10 +556,18 @@ return AddressAsInt(cast_ptr_to_adr(fieldadr)) return NotImplemented def __repr__(self): + clsname = self.__class__.__name__ try: - return '<AddressAsInt %s>' % (self.adr.ptr,) + return '<%s %s>' % (clsname, self.adr.ptr) except AttributeError: - return '<AddressAsInt at 0x%x>' % (uid(self),) + return '<%s at 0x%x>' % (clsname, uid(self)) + +class AddressAsUInt(AddressAsInt): + def annotation(self): + from pypy.annotation import model + return model.SomeInteger(unsigned=True) + def lltype(self): + return lltype.Unsigned # ____________________________________________________________ @@ -678,6 +693,9 @@ res = cast(lltype.Signed, res) return res +def cast_adr_to_uint_symbolic(adr): + return adr._cast_to_uint() + _NONGCREF = lltype.Ptr(lltype.OpaqueType('NONGCREF')) def cast_int_to_adr(int): if isinstance(int, AddressAsInt): 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 @@ -119,8 +119,9 @@ GCFLAG_POSSIBLY_OUTDATED = first_gcflag << 1 # keep in sync with et.h GCFLAG_NOT_WRITTEN = first_gcflag << 2 # keep in sync with et.h GCFLAG_LOCAL_COPY = first_gcflag << 3 # keep in sync with et.h -GCFLAG_HAS_SHADOW = first_gcflag << 4 -GCFLAG_FIXED_HASH = first_gcflag << 5 +GCFLAG_VISITED = first_gcflag << 4 +GCFLAG_HAS_SHADOW = first_gcflag << 5 +GCFLAG_FIXED_HASH = first_gcflag << 6 def always_inline(fn): @@ -188,8 +189,8 @@ # The following line causes the _stm_getsize() function to be # generated in the C source with a specific signature, where it # can be called by the C code. - llop.nop(lltype.Void, llhelper(self.stm_operations.GETSIZE, - self._stm_getsize)) + llop.nop(lltype.Void, llhelper(self.stm_operations.DUPLICATE, + self._stm_duplicate)) # self.sharedarea.setup() # @@ -278,7 +279,7 @@ def collect(self, gen=1): self.get_tls().local_collection() if gen > 0: - debug_print("XXX not doing a global collect()") + debug_print("XXX not implemented: global collect()") def start_transaction(self): self.get_tls().start_transaction() @@ -305,6 +306,12 @@ flags |= GCFLAG_GLOBAL self.init_gc_object(addr, typeid16, flags) + def obj_revision(obj): + return hdr_revision(self.header(obj)) + + def set_obj_revision(obj, nrevision): + set_hdr_revision(self.header(obj), nrevision) + def stm_duplicate(self, obj): size_gc_header = self.gcheaderbuilder.size_gc_header size = self.get_size(obj) @@ -357,11 +364,11 @@ # # Update the header of the local 'obj' hdr.tid |= GCFLAG_HAS_SHADOW - hdr.version = fixedobj + set_hdr_revision(hdr, fixedobj) # else: # There is already a corresponding fixedobj - fixedobj = hdr.version + fixedobj = hdr_revision(hdr) # obj = fixedobj # @@ -391,3 +398,13 @@ def identityhash(self, gcobj): return self.id_or_identityhash(gcobj, True) + + +# ____________________________________________________________ +# helpers + +def hdr_revision(hdr): + return llmemory.cast_int_to_adr(hdr.revision) + +def set_hdr_revision(hdr, nrevision): + hdr.revision = llmemory.cast_adr_to_uint_symbolic(nrevision) diff --git a/pypy/rpython/memory/gc/stmshared.py b/pypy/rpython/memory/gc/stmshared.py --- a/pypy/rpython/memory/gc/stmshared.py +++ b/pypy/rpython/memory/gc/stmshared.py @@ -52,5 +52,9 @@ self.free_object(obj) obj = next + def free_and_clear_list(self, lst): + while lst.non_empty(): + self.free_object(lst.pop()) + def delete(self): free_non_gc_object(self) diff --git a/pypy/rpython/memory/gc/stmtls.py b/pypy/rpython/memory/gc/stmtls.py --- a/pypy/rpython/memory/gc/stmtls.py +++ b/pypy/rpython/memory/gc/stmtls.py @@ -10,7 +10,9 @@ from pypy.rpython.memory.gc.stmgc import WORD, NULL from pypy.rpython.memory.gc.stmgc import always_inline, dont_inline from pypy.rpython.memory.gc.stmgc import GCFLAG_GLOBAL, GCFLAG_VISITED -from pypy.rpython.memory.gc.stmgc import GCFLAG_WAS_COPIED, GCFLAG_HAS_SHADOW +from pypy.rpython.memory.gc.stmgc import GCFLAG_LOCAL_COPY, GCFLAG_HAS_SHADOW +from pypy.rpython.memory.gc.stmgc import GCFLAG_POSSIBLY_OUTDATED +from pypy.rpython.memory.gc.stmgc import hdr_revision class StmGCTLS(object): @@ -41,7 +43,9 @@ # --- a thread-local allocator for the shared area from pypy.rpython.memory.gc.stmshared import StmGCThreadLocalAllocator self.sharedarea_tls = StmGCThreadLocalAllocator(self.gc.sharedarea) - # --- the LOCAL objects with GCFLAG_WAS_COPIED + # --- the GCFLAG_LOCAL_COPY objects are also allocated with + # self.sharedarea_tls, but we need their 'revision', so we + # can't do add_regular() on them. They go here instead: self.copied_local_objects = self.AddressStack() # --- the LOCAL objects which are weakrefs. They are also listed # in the appropriate place, like sharedarea_tls, if needed. @@ -122,7 +126,7 @@ # malloced objects will be LOCAL. if self.gc.DEBUG: self.check_all_global_objects() - self.relocalize_from_stack() + #self.relocalize_from_stack() def stop_transaction(self): """Stop a transaction: do a local collection to empty the @@ -157,9 +161,9 @@ debug_start("gc-local") # if end_of_transaction: - self.detect_flag_combination = GCFLAG_WAS_COPIED | GCFLAG_VISITED + self.replace_pointers_following_revision = GCFLAG_LOCAL_COPY else: - self.detect_flag_combination = -1 + self.replace_pointers_following_revision = 0 # # Move away the previous sharedarea_tls and start a new one. from pypy.rpython.memory.gc.stmshared import StmGCThreadLocalAllocator @@ -251,6 +255,7 @@ return localobj def fresh_new_weakref(self, obj): + XXX # review self.local_weakrefs.append(obj) # ------------------------------------------------------------ @@ -258,7 +263,8 @@ def _promote_locals_to_globals(self): ll_assert(self.local_nursery_is_empty(), "nursery must be empty [1]") # - # Promote all objects in sharedarea_tls to global + # Promote all objects in sharedarea_tls to global. + # This is the "real" equivalent of _FakeReach() in et.c. obj = self.sharedarea_tls.chained_list self.sharedarea_tls.chained_list = NULL # @@ -267,18 +273,19 @@ obj = hdr.version ll_assert(hdr.tid & GCFLAG_GLOBAL == 0, "already GLOBAL [1]") ll_assert(hdr.tid & GCFLAG_VISITED == 0, "unexpected VISITED [1]") - hdr.tid |= GCFLAG_GLOBAL - self._clear_version_for_global_object(hdr) + hdr.tid |= GCFLAG_GLOBAL | GCFLAG_NOT_WRITTEN + if hdr.tid & GCFLAG_LOCAL_COPY == 0: + self._clear_version_for_global_object(hdr) def _clear_version_for_global_object(self, hdr): # Reset the 'version' to initialize a newly global object. - # When translated with C code, we set it to NULL (version 0). + # When translated with C code, we set it to 1. # When non-translated, we reset it instead to '_uninitialized' # to simulate the fact that the C code might change it. if we_are_translated(): - hdr.version = NULL + hdr.revision = r_uint(1) else: - del hdr.version + del hdr.revision def _cleanup_state(self): #if self.rawmalloced_objects: @@ -287,57 +294,50 @@ # free the old unused local objects still allocated in the # StmGCThreadLocalAllocator self.sharedarea_tls.free_and_clear() - # free the local copies. Note that commonly, they are leftovers - # from the previous transaction running in this thread. The C code - # has just copied them over the corresponding GLOBAL objects at the - # very end of that transaction. - self._free_and_clear_list(self.copied_local_objects) + # free these ones too + self.sharedarea_tls.free_and_clear_list(self.copied_local_objects) # forget the local weakrefs. self.local_weakrefs.clear() - def _free_and_clear_list(self, lst): - while lst.non_empty(): - self.sharedarea_tls.free_object(lst.pop()) - def collect_roots_from_stack(self, end_of_transaction): - if end_of_transaction: - # in this mode, we flag the reference to local objects in order - # to re-localize them when we later start the next transaction - # using this section of the shadowstack - self.gc.root_walker.walk_current_stack_roots( - StmGCTLS._trace_drag_out_and_flag_local, self) - else: +## if end_of_transaction: +## # in this mode, we flag the reference to local objects in order +## # to re-localize them when we later start the next transaction +## # using this section of the shadowstack +## self.gc.root_walker.walk_current_stack_roots( +## StmGCTLS._trace_drag_out_and_flag_local, self) +## else: self.gc.root_walker.walk_current_stack_roots( StmGCTLS._trace_drag_out1, self) - def _trace_drag_out_and_flag_local(self, root): - x = llmemory.cast_adr_to_int(root.address[0]) - if x & 2: - root.address[0] -= 2 - old_tid = self.gc.header(root.address[0]).tid - # - self._trace_drag_out1(root) - # - # if root.address[0] used to point to a local object, then flag - # this by adding the bit of value 2. - if old_tid & GCFLAG_GLOBAL == 0: - obj = root.address[0] - x = llmemory.cast_adr_to_int(obj) - ll_assert(x & 3 == 0, "flag_local: misaligned obj") - obj = llarena.getfakearenaaddress(obj) - root.address[0] = obj + 2 +## def _trace_drag_out_and_flag_local(self, root): +## x = llmemory.cast_adr_to_int(root.address[0]) +## if x & 2: +## root.address[0] -= 2 +## old_tid = self.gc.header(root.address[0]).tid +## # +## self._trace_drag_out1(root) +## # +## # if root.address[0] used to point to a local object, then flag +## # this by adding the bit of value 2. +## if old_tid & GCFLAG_GLOBAL == 0: +## obj = root.address[0] +## x = llmemory.cast_adr_to_int(obj) +## ll_assert(x & 3 == 0, "flag_local: misaligned obj") +## obj = llarena.getfakearenaaddress(obj) +## root.address[0] = obj + 2 - def relocalize_from_stack(self): - self.gc.root_walker.walk_current_stack_roots( - StmGCTLS._relocalize_from_stack, self) +## def relocalize_from_stack(self): +## self.gc.root_walker.walk_current_stack_roots( +## StmGCTLS._relocalize_from_stack, self) - def _relocalize_from_stack(self, root): - x = llmemory.cast_adr_to_int(root.address[0]) - if x & 2: - obj = root.address[0] - 2 - localobj = self.gc.stm_writebarrier(obj) - root.address[0] = localobj +## def _relocalize_from_stack(self, root): +## x = llmemory.cast_adr_to_int(root.address[0]) +## if x & 2: +## obj = root.address[0] - 2 +## localobj = self.gc.stm_writebarrier(obj) +## root.address[0] = localobj def collect_from_raw_structures(self): self.gc.root_walker.walk_current_nongc_roots( @@ -360,19 +360,19 @@ """Return the current surviving state of the object: 0: not marked as surviving, so far 1: survives and does not move - 2: survives, but moves to 'hdr.version' + 2: survives, but moves to 'hdr.revision' """ hdr = self.gc.header(obj) flag_combination = hdr.tid & (GCFLAG_GLOBAL | - GCFLAG_WAS_COPIED | + GCFLAG_LOCAL_COPY | GCFLAG_VISITED) if flag_combination == 0: return 0 # not marked as surviving, so far - if flag_combination == self.detect_flag_combination: - # At a normal time, self.detect_flag_combination is -1 - # and this case is never seen. At end of transactions, - # detect_flag_combination is GCFLAG_WAS_COPIED|GCFLAG_VISITED. + if flag_combination & self.replace_pointers_following_revision: + # At a normal time, self.replace_pointers_following_revision + # is 0 and this case is never seen. At end of transactions, + # detect_flag_combination is GCFLAG_LOCAL_COPY. # This case is to force pointers to the LOCAL copy to be # replaced with pointers to the GLOBAL copy. return 2 @@ -484,19 +484,19 @@ def _stm_enum_callback(tlsaddr, globalobj, localobj): self = StmGCTLS.cast_address_to_tls_object(tlsaddr) localhdr = self.gc.header(localobj) - ll_assert(localhdr.version == globalobj, + ll_assert(hdr_revision(localhdr) == globalobj, "in a root: localobj.version != globalobj") ll_assert(localhdr.tid & GCFLAG_GLOBAL == 0, "in a root: unexpected GCFLAG_GLOBAL") - ll_assert(localhdr.tid & GCFLAG_WAS_COPIED != 0, - "in a root: missing GCFLAG_WAS_COPIED") - ll_assert(localhdr.tid & GCFLAG_VISITED != 0, - "in a root: missing GCFLAG_VISITED") + ll_assert(localhdr.tid & GCFLAG_LOCAL_COPY != 0, + "in a root: missing GCFLAG_LOCAL_COPY") + ll_assert(localhdr.tid & GCFLAG_VISITED == 0, + "in a root: unexpected GCFLAG_VISITED") globalhdr = self.gc.header(globalobj) ll_assert(globalhdr.tid & GCFLAG_GLOBAL != 0, "in a root: GLOBAL: missing GCFLAG_GLOBAL") - ll_assert(globalhdr.tid & GCFLAG_WAS_COPIED != 0, - "in a root: GLOBAL: missing GCFLAG_WAS_COPIED") + ll_assert(globalhdr.tid & GCFLAG_POSSIBLY_OUTDATED != 0, + "in a root: GLOBAL: missing GCFLAG_POSSIBLY_OUTDATED") ll_assert(globalhdr.tid & GCFLAG_VISITED == 0, "in a root: GLOBAL: unexpected GCFLAG_VISITED") TL = lltype.cast_primitive(lltype.Signed, @@ -505,7 +505,8 @@ self.gc.get_type_id(globalobj)) ll_assert(TL == TG, "in a root: type(LOCAL) != type(GLOBAL)") # - self.trace_and_drag_out_of_nursery(localobj) + localhdr.tid |= GCFLAG_VISITED + self.pending.append(localobj) def collect_flush_pending(self): # Follow the objects in the 'pending' stack and move the @@ -566,8 +567,8 @@ def _debug_check_all_global_from_stack_1(self, root): obj = root.address[0] - if llmemory.cast_adr_to_int(obj) & 2: - obj -= 2 # will be fixed by relocalize_from_stack() +## if llmemory.cast_adr_to_int(obj) & 2: +## obj -= 2 # will be fixed by relocalize_from_stack() self._debug_check_all_global_obj(obj) def _debug_check_all_global(self, root, ignored): 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,8 +1,10 @@ import py 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.gc.stmgc import GCFLAG_VISITED +from pypy.rpython.memory.gc.stmgc import GCFLAG_GLOBAL, GCFLAG_NOT_WRITTEN +from pypy.rpython.memory.gc.stmgc import GCFLAG_POSSIBLY_OUTDATED +from pypy.rpython.memory.gc.stmgc import GCFLAG_LOCAL_COPY +from pypy.rpython.memory.gc.stmgc import hdr_revision, set_hdr_revision from pypy.rpython.memory.support import mangle_hash @@ -86,20 +88,20 @@ for key, value in self._tldicts[self.threadnum].iteritems(): callback(tls, key, value) - def read_attribute(self, obj, name): - obj = llmemory.cast_ptr_to_adr(obj) - hdr = self._gc.header(obj) - localobj = self.tldict_lookup(obj) - if localobj == llmemory.NULL: - localobj = obj - else: - assert hdr.tid & GCFLAG_GLOBAL != 0 - localobj = llmemory.cast_adr_to_ptr(localobj, lltype.Ptr(SR)) - return getattr(localobj, name) +## def read_attribute(self, obj, name): +## obj = llmemory.cast_ptr_to_adr(obj) +## hdr = self._gc.header(obj) +## localobj = self.tldict_lookup(obj) +## if localobj == llmemory.NULL: +## localobj = obj +## else: +## assert hdr.tid & GCFLAG_GLOBAL != 0 +## localobj = llmemory.cast_adr_to_ptr(localobj, lltype.Ptr(SR)) +## return getattr(localobj, name) - def stm_copy_transactional_to_raw(self, srcobj, dstobj, size): - llmemory.raw_memcopy(srcobj, dstobj, size) - self._transactional_copies.append((srcobj, dstobj)) +## def stm_copy_transactional_to_raw(self, srcobj, dstobj, size): +## llmemory.raw_memcopy(srcobj, dstobj, size) +## self._transactional_copies.append((srcobj, dstobj)) def fake_get_size(obj): @@ -179,7 +181,8 @@ 1) llarena.arena_reserve(adr1, totalsize) addr = adr1 + self.gc.gcheaderbuilder.size_gc_header - self.gc.header(addr).tid = self.gc.combine(tid, GCFLAG_GLOBAL) + self.gc.header(addr).tid = self.gc.combine( + tid, GCFLAG_GLOBAL | GCFLAG_NOT_WRITTEN) realobj = llmemory.cast_adr_to_ptr(addr, lltype.Ptr(STRUCT)) else: gcref = self.gc.malloc_fixedsize_clear(tid, size, @@ -205,9 +208,27 @@ assert (hdr.tid & GCFLAG_WAS_COPIED != 0) == must_have_was_copied if must_have_version != '?': assert hdr.version == must_have_version - def read_signed(self, obj, offset): - meth = getattr(self.gc, 'read_int%d' % WORD) - return meth(obj, offset) + + def stm_writebarrier(self, P): + if lltype.typeOf(P) != llmemory.Address: + P = llmemory.cast_ptr_to_adr(P) + hdr = self.gc.header(P) + if hdr.tid & GCFLAG_NOT_WRITTEN == 0: + # already a local, written-to object + assert hdr.tid & GCFLAG_GLOBAL == 0 + assert hdr.tid & GCFLAG_POSSIBLY_OUTDATED == 0 + W = P + else: + # slow case of the write barrier + if hdr.tid & GCFLAG_GLOBAL == 0: + W = P + R = hdr_revision(hdr) + else: + R = P + W = self.gc.stm_operations.tldict_lookup(R) + self.gc.header(W).tid &= ~GCFLAG_NOT_WRITTEN + self.gc.header(R).tid |= GCFLAG_POSSIBLY_OUTDATED + return W class TestBasic(StmGCTests): @@ -246,18 +267,20 @@ def test_write_barrier_exists(self): self.select_thread(1) t, t_adr = self.malloc(S) - obj = self.gc.stm_writebarrier(t_adr) # local object + obj = self.stm_writebarrier(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 | GCFLAG_VISITED - self.gc.header(t_adr).version = s_adr + assert self.gc.header(s_adr).tid & GCFLAG_GLOBAL != 0 + assert self.gc.header(t_adr).tid & GCFLAG_GLOBAL == 0 + self.gc.header(s_adr).tid |= GCFLAG_POSSIBLY_OUTDATED + self.gc.header(t_adr).tid |= GCFLAG_LOCAL_COPY + set_hdr_revision(self.gc.header(t_adr), s_adr) self.gc.stm_operations._tldicts[1][s_adr] = t_adr - obj = self.gc.stm_writebarrier(s_adr) # global copied object + obj = self.stm_writebarrier(s_adr) # global copied object assert obj == t_adr assert self.gc.stm_operations._transactional_copies == [] _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit