[pypy-commit] pypy stm-gc: id(), hash(), and small refactors
arigo
noreply at buildbot.pypy.org
Mon Apr 16 18:05:05 CEST 2012
Author: Armin Rigo <arigo at 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):
More information about the pypy-commit
mailing list