Author: Armin Rigo <ar...@tunes.org> Branch: stm-gc Changeset: r54429:48a8fcb4017c Date: 2012-04-16 17:42 +0200 http://bitbucket.org/pypy/pypy/changeset/48a8fcb4017c/
Log: id(), hash(), and small refactors 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 @@ -319,8 +319,8 @@ ll_assert(hdr.tid & GCFLAGS == GCFLAGS, "stm_write: bogus flags on source object") # - # Remove the GCFLAG_GLOBAL from the copy - localhdr.tid = hdr.tid & ~GCFLAG_GLOBAL + # Remove the GCFLAG_GLOBAL from the copy, and add GCFLAG_VISITED + localhdr.tid = hdr.tid + (GCFLAG_VISITED - GCFLAG_GLOBAL) # # Set the 'version' field of the local copy to be a pointer # to the global obj. (The field is called 'version' because @@ -357,35 +357,43 @@ """Implement the common logic of id() and identityhash() of an object, given as a GCREF. """ - raise NotImplementedError obj = llmemory.cast_ptr_to_adr(gcobj) hdr = self.header(obj) - # - if hdr.tid & GCFLAG_GLOBAL == 0: + tls = self.get_tls() + if tls.is_in_nursery(obj): # - # The object is a local object. Find or allocate a corresponding - # global object. - if hdr.tid & (GCFLAG_WAS_COPIED | GCFLAG_HAS_SHADOW) == 0: + # The object is still in the nursery of the current TLS. + # (It cannot be in the nursery of a different thread, because + # such objects are not visible to different threads at all.) + # + ll_assert(hdr.tid & GCFLAG_WAS_COPIED == 0, "id: WAS_COPIED?") + # + if hdr.tid & GCFLAG_HAS_SHADOW == 0: # # We need to allocate a global object here. We only allocate # it for now; it is left completely uninitialized. + size_gc_header = self.gcheaderbuilder.size_gc_header size = self.get_size(obj) - tls = self.collector.get_tls() - globalobj = self._malloc_global_raw(tls, size) - self.header(globalobj).tid = GCFLAG_GLOBAL + totalsize = size_gc_header + size + fixedobj = tls.sharedarea_tls.malloc_object(totalsize) + tls.sharedarea_tls.add_regular(fixedobj) + self.header(fixedobj).tid = 0 # GCFLAG_VISITED is off # # Update the header of the local 'obj' hdr.tid |= GCFLAG_HAS_SHADOW - hdr.version = globalobj + hdr.version = fixedobj # else: - # There is already a corresponding globalobj - globalobj = hdr.version + # There is already a corresponding fixedobj + fixedobj = hdr.version # - obj = globalobj + obj = fixedobj + # + elif hdr.tid & (GCFLAG_GLOBAL|GCFLAG_WAS_COPIED) == GCFLAG_WAS_COPIED: + # + # The object is the local copy of a LOCAL-GLOBAL pair. + obj = hdr.version # - ll_assert(self.header(obj).tid & GCFLAG_GLOBAL != 0, - "id_or_identityhash: unexpected local object") i = llmemory.cast_adr_to_int(obj) if is_hash: # For identityhash(), we need a special case for some 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 @@ -25,7 +25,6 @@ self.gc = sharedarea.gc self.sharedarea = sharedarea self.chained_list = NULL - self.special_stack = self.gc.AddressStack() def malloc_object(self, totalsize): """Malloc. You must also call add_regular() or add_special() later.""" @@ -40,10 +39,6 @@ hdr.version = self.chained_list self.chained_list = obj - def add_special(self, obj): - """After malloc_object(), register the object in a separate stack.""" - self.special_stack.append(obj) - def free_object(self, adr2): adr1 = adr2 - self.gc.gcheaderbuilder.size_gc_header llarena.arena_free(llarena.getfakearenaaddress(adr1)) @@ -55,10 +50,6 @@ next = self.gc.header(obj).version self.free_object(obj) obj = next - s = self.special_stack - while s.non_empty(): - self.free_object(s.pop()) def delete(self): - self.special_stack.delete() 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 @@ -9,7 +9,7 @@ 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 +from pypy.rpython.memory.gc.stmgc import GCFLAG_WAS_COPIED, GCFLAG_HAS_SHADOW class StmGCTLS(object): @@ -49,12 +49,16 @@ # --- 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 + self.copied_local_objects = self.AddressStack() # self._register_with_C_code() def teardown_thread(self): self._cleanup_state() self._unregister_with_C_code() + self.copied_local_objects.delete() + self.sharedarea_tls.delete() self._free_nursery(self.nursery_start) free_non_gc_object(self) @@ -241,7 +245,7 @@ """Allocate an object that will be used as a LOCAL copy of some GLOBAL object.""" localobj = self.sharedarea_tls.malloc_object(totalsize) - self.sharedarea_tls.add_special(localobj) + self.copied_local_objects.append(localobj) return localobj # ------------------------------------------------------------ @@ -264,9 +268,18 @@ #if self.rawmalloced_objects: # xxx # free the rawmalloced_objects still around - # if we still have a StmGCThreadLocalAllocator, free the old unused - # local objects it still contains + # free the old unused local objects still allocated in the + # StmGCThreadLocalAllocator self.sharedarea_tls.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) + + def _free_and_clear_list(self, lst): + while lst.non_empty(): + self.sharedarea_tls.free_object(lst.pop()) def collect_roots_from_stack(self): @@ -293,40 +306,58 @@ obj = root.address[0] hdr = self.gc.header(obj) # - # If 'obj' is a LOCAL copy of a GLOBAL object, skip it - # (this case is handled differently in collect_roots_from_tldict) - if hdr.tid & GCFLAG_WAS_COPIED: - return - # # If 'obj' is not in the nursery, we set GCFLAG_VISITED if not self.is_in_nursery(obj): if hdr.tid & GCFLAG_VISITED == 0: + ll_assert(hdr.tid & GCFLAG_WAS_COPIED == 0, + "local GCFLAG_WAS_COPIED without GCFLAG_VISITED") hdr.tid |= GCFLAG_VISITED self.pending.append(obj) return # # If 'obj' was already forwarded, change it to its forwarding address. - if hdr.tid & GCFLAG_VISITED: - root.address[0] = hdr.version - return - # - # First visit to 'obj': we must move this YOUNG obj out of the nursery. - size_gc_header = self.gc.gcheaderbuilder.size_gc_header - size = self.gc.get_size(obj) - totalsize = size_gc_header + size - # - # Common case: allocate a new nonmovable location for it. - newobj = self._malloc_out_of_nursery(totalsize) - # - # Copy it. Note that references to other objects in the - # nursery are kept unchanged in this step. - llmemory.raw_memcopy(obj - size_gc_header, - newobj - size_gc_header, - totalsize) - # - # Register the object here, not before the memcopy() that would - # overwrite its 'version' field - self._register_newly_malloced_obj(newobj) + # If 'obj' has already a shadow but isn't forwarded so far, use it. + if hdr.tid & (GCFLAG_VISITED | GCFLAG_HAS_SHADOW): + # + if hdr.tid & GCFLAG_VISITED: + root.address[0] = hdr.version + return + # + # Case of GCFLAG_HAS_SHADOW. See comments below. + size_gc_header = self.gc.gcheaderbuilder.size_gc_header + size = self.gc.get_size(obj) + totalsize = size_gc_header + size + hdr.tid &= ~GCFLAG_HAS_SHADOW + newobj = hdr.version + newhdr = self.gc.header(newobj) + # + saved_version = newhdr.version + llmemory.raw_memcopy(obj - size_gc_header, + newobj - size_gc_header, + totalsize) + newhdr.version = saved_version + newhdr.tid = hdr.tid | GCFLAG_VISITED + # + else: + # + # First visit to 'obj': we must move this YOUNG obj out of the + # nursery. + size_gc_header = self.gc.gcheaderbuilder.size_gc_header + size = self.gc.get_size(obj) + totalsize = size_gc_header + size + # + # Common case: allocate a new nonmovable location for it. + newobj = self._malloc_out_of_nursery(totalsize) + # + # Copy it. Note that references to other objects in the + # nursery are kept unchanged in this step. + llmemory.raw_memcopy(obj - size_gc_header, + newobj - size_gc_header, + totalsize) + # + # Register the object here, not before the memcopy() that would + # overwrite its 'version' field + self._register_newly_malloced_obj(newobj) # # Set the YOUNG copy's GCFLAG_VISITED and set its version to # point to the OLD copy. @@ -366,6 +397,8 @@ "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") # self.trace_and_drag_out_of_nursery(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 @@ -167,14 +167,21 @@ def malloc(self, STRUCT, weakref=False, globl='auto'): size = llarena.round_up_for_allocation(llmemory.sizeof(STRUCT)) 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) if globl == 'auto': globl = (self.gc.stm_operations.threadnum == 0) if globl: - self.gc.header(addr).tid |= GCFLAG_GLOBAL + totalsize = self.gc.gcheaderbuilder.size_gc_header + size + adr1 = llarena.arena_malloc(llmemory.raw_malloc_usage(totalsize), + 1) + llarena.arena_reserve(adr1, totalsize) + addr = adr1 + self.gc.gcheaderbuilder.size_gc_header + self.gc.header(addr).tid = GCFLAG_GLOBAL + realobj = llmemory.cast_adr_to_ptr(addr, lltype.Ptr(STRUCT)) + else: + 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 def select_thread(self, threadnum): self.gc.stm_operations.threadnum = threadnum @@ -340,6 +347,7 @@ assert main_tls.nursery_free == main_tls.nursery_start # empty def test_commit_transaction_no_references(self): + py.test.skip("rewrite me") s, s_adr = self.malloc(S) s.b = 12345 self.select_thread(1) @@ -448,6 +456,7 @@ s, s_adr = self.malloc(S) self.select_thread(1) t_adr = self.gc.stm_writebarrier(s_adr) # make a local copy + assert t_adr != s_adr t = llmemory.cast_adr_to_ptr(t_adr, llmemory.GCREF) i = self.gc.id(t) assert i == llmemory.cast_adr_to_int(s_adr) @@ -465,8 +474,11 @@ def test_id_of_local_surviving(self): sr1, sr1_adr = self.malloc(SR) + assert sr1.s1 == lltype.nullptr(S) + assert sr1.sr2 == lltype.nullptr(SR) self.select_thread(1) t2, t2_adr = self.malloc(S) + t2.a = 423 tr1_adr = self.gc.stm_writebarrier(sr1_adr) assert tr1_adr != sr1_adr tr1 = llmemory.cast_adr_to_ptr(tr1_adr, lltype.Ptr(SR)) @@ -478,7 +490,7 @@ assert i == self.gc.id(t2) self.gc.commit_transaction() s2 = tr1.s1 # tr1 is a root, so not copied yet - assert s2 and s2 != t2 + assert s2 and s2.a == 423 and s2._obj0 != t2._obj0 assert self.gc.id(s2) == i def test_hash_of_global(self): @@ -509,6 +521,7 @@ sr1, sr1_adr = self.malloc(SR) self.select_thread(1) t2, t2_adr = self.malloc(S) + t2.a = 424 tr1_adr = self.gc.stm_writebarrier(sr1_adr) assert tr1_adr != sr1_adr tr1 = llmemory.cast_adr_to_ptr(tr1_adr, lltype.Ptr(SR)) @@ -521,7 +534,7 @@ assert i == self.gc.identityhash(t2) self.gc.commit_transaction() s2 = tr1.s1 # tr1 is a root, so not copied yet - assert s2 and s2 != t2 + assert s2 and s2.a == 424 and s2._obj0 != t2._obj0 assert self.gc.identityhash(s2) == i def test_weakref_to_global(self): _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit