[pypy-commit] pypy stm-gc: Progress
arigo
noreply at buildbot.pypy.org
Sun Apr 15 15:09:58 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch: stm-gc
Changeset: r54369:ddb7a8bdd471
Date: 2012-04-15 15:09 +0200
http://bitbucket.org/pypy/pypy/changeset/ddb7a8bdd471/
Log: Progress
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
@@ -44,6 +44,7 @@
GCFLAG_FIXED_HASH = first_gcflag << 3
GCFLAG_WEAKREF = first_gcflag << 4
GCFLAG_VISITED = first_gcflag << 5
+GCFLAG_MOVED = first_gcflag << 6
def always_inline(fn):
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
@@ -27,27 +27,22 @@
self.chained_list = NULL
self.special_stack = self.gc.AddressStack()
- def delete(self):
- self.special_stack.delete()
- free_non_gc_object(self)
+ def malloc_object(self, totalsize):
+ """Malloc. You must also call add_regular() or add_special() later."""
+ adr1 = llarena.arena_malloc(llmemory.raw_malloc_usage(totalsize), 0)
+ llarena.arena_reserve(adr1, totalsize)
+ return adr1 + self.gc.gcheaderbuilder.size_gc_header
- def malloc_regular(self, size):
- """Malloc for an object where the 'version' field can be used
- internally for a chained list."""
- adr1 = llarena.arena_malloc(size, 0)
- adr2 = adr1 + self.gc.gcheaderbuilder
- hdr = llmemory.cast_adr_to_ptr(adr1, lltype.Ptr(self.gc.HDR))
+ def add_regular(self, obj):
+ """After malloc_object(), register the object in the internal chained
+ list. For objects whose 'version' field is not otherwise needed."""
+ hdr = self.gc.header(obj)
hdr.version = self.chained_list
- self.chained_list = adr2
- return adr2
+ self.chained_list = obj
- def malloc_special(self, size):
- """Malloc for an object where the 'version' field cannot be
- used internally. It's the rare case here."""
- adr1 = llarena.arena_malloc(size, 0)
- adr2 = adr1 + self.gc.gcheaderbuilder.size_gc_header
- self.special_stack.append(adr2)
- return adr2
+ 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
@@ -56,3 +51,18 @@
def replace_special_stack(self, new_special_stack):
self.special_stack.delete()
self.special_stack = new_special_stack
+
+ def clear(self):
+ obj = self.chained_list
+ self.chained_list = NULL
+ while obj:
+ 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
@@ -8,6 +8,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_MOVED
class StmGCTLS(object):
@@ -19,7 +20,6 @@
nontranslated_dict = {}
def __init__(self, gc, in_main_thread):
- from pypy.rpython.memory.gc.stmshared import StmGCThreadLocalAllocator
self.gc = gc
self.in_main_thread = in_main_thread
self.stm_operations = self.gc.stm_operations
@@ -46,7 +46,7 @@
#self.old_objects_with_weakrefs = NULL
#
# --- a thread-local allocator for the shared area
- self.sharedarea_tls = StmGCThreadLocalAllocator(gc.sharedarea)
+ self.sharedarea_tls = None
#
self._register_with_C_code()
@@ -153,37 +153,41 @@
#
debug_start("gc-local")
#
- # Linked list of LOCAL objects pending a visit. Note that no
- # GLOBAL object can at any point contain a reference to a LOCAL
- # object.
- self.pending_list = NULL
+ # Move away the previous sharedarea_tls and start a new one.
+ from pypy.rpython.memory.gc.stmshared import StmGCThreadLocalAllocator
+ previous_sharedarea_tls = self.sharedarea_tls # may be None
+ self.sharedarea_tls = StmGCThreadLocalAllocator(self.gc.sharedarea)
+ #
+ # List of LOCAL objects pending a visit. Note that no GLOBAL
+ # object can at any point contain a reference to a LOCAL object.
+ self.pending = self.AddressStack()
#
# First, find the roots that point to LOCAL objects. All YOUNG
# (i.e. nursery) objects found are copied out of the nursery.
- # All OLD objects found are flagged with GCFLAG_VISITED. At this
- # point, the content of the objects is not modified; the objects
- # are merely added to the chained list 'pending_list'.
- self.collect_roots_in_nursery()
+ # All OLD objects found are flagged with GCFLAG_VISITED.
+ # At this point, the content of the objects is not modified;
+ # they are simply added to 'pending'.
+ self.collect_roots_from_stack()
#
# Also find the roots that are the local copy of GCFLAG_WAS_COPIED
# objects.
self.collect_roots_from_tldict()
#
- # Now repeat following objects until 'pending_list' is empty.
- self.collect_oldrefs_to_nursery()
+ # Now repeatedly follow objects until 'pending' is empty.
+ self.collect_flush_pending()
#
# Walk the list of LOCAL raw-malloced objects, and free them if
# necessary.
#self.free_local_rawmalloced_objects()
#
- # Ask the ArenaCollection to visit all objects. Free the ones
- # that have not been visited above, and reset GCFLAG_VISITED on
- # the others.
- self.ac.mass_free(self._free_if_unvisited)
+ # Visit all previous OLD objects. Free the ones that have not been
+ # visited above, and reset GCFLAG_VISITED on the others.
+ if previous_sharedarea_tls is not None:
+ self.mass_free_old_local(previous_sharedarea_tls)
#
# All live nursery objects are out, and the rest dies. Fill
# the whole nursery with zero and reset the current nursery pointer.
- llarena.arena_reset(self.nursery, self.nursery_size, 2)
+ llarena.arena_reset(self.nursery_start, self.nursery_size, 2)
self.nursery_free = self.nursery_start
#
debug_stop("gc-local")
@@ -215,7 +219,7 @@
def allocate_object_of_size(self, size):
if not self.nursery_free:
fatalerror("malloc in a non-main thread but outside a transaction")
- if size > self.nursery_size // 8 * 7:
+ if llmemory.raw_malloc_usage(size) > self.nursery_size // 8 * 7:
fatalerror("object too large to ever fit in the nursery")
while True:
self.local_collection()
@@ -225,6 +229,11 @@
continue # try again
return free
+ def is_in_nursery(self, addr):
+ ll_assert(llmemory.cast_adr_to_int(addr) & 1 == 0,
+ "odd-valued (i.e. tagged) pointer unexpected here")
+ return self.nursery_start <= addr < self.nursery_top
+
# ------------------------------------------------------------
def _promote_locals_to_globals(self):
@@ -245,5 +254,102 @@
## obj = hdr.version
def _cleanup_state(self):
- if self.rawmalloced_objects:
- xxx # free the rawmalloced_objects still around
+ #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
+ if self.sharedarea_tls is not None:
+ self.sharedarea_tls.clear()
+ self.sharedarea_tls.delete()
+ self.sharedarea_tls = None
+
+
+ def collect_roots_from_stack(self):
+ self.gc.root_walker.walk_roots(
+ StmGCTLS._trace_drag_out1, # stack roots of the current thread
+ None, # static in prebuilt non-gc
+ None, # static in prebuilt gc
+ self)
+
+ def _trace_drag_out1(self, root):
+ self._trace_drag_out(root, None)
+
+ def _trace_drag_out(self, root, ignored):
+ """Trace callback: 'root' is the address of some pointer. If that
+ pointer points to a YOUNG object, allocate an OLD copy of it and
+ fix the pointer. Also, add the object to 'pending_list', if it was
+ not done so far.
+ """
+ obj = root.address[0]
+ hdr = self.gc.header(obj)
+ #
+ # If 'obj' is not in the nursery, we set GCFLAG_VISITED
+ if not self.is_in_nursery(obj):
+ if hdr.tid & GCFLAG_VISITED == 0:
+ hdr.tid |= GCFLAG_VISITED
+ self.pending.append(obj)
+ return
+ #
+ # If 'obj' was already forwarded, change it to its forwarding address.
+ if hdr.tid & GCFLAG_MOVED:
+ 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)
+ #
+ # Set the YOUNG copy's GCFLAG_MOVED and set its version to
+ # point to the OLD copy.
+ hdr.tid |= GCFLAG_MOVED
+ hdr.version = newobj
+ #
+ # Change the original pointer to this object.
+ root.address[0] = newobj
+ #
+ # Add the newobj to the list 'pending', because it can contain
+ # further pointers to other young objects. We will fix such
+ # references to point to the copy of the young objects when we
+ # walk 'pending_list'.
+ self.pending.append(newobj)
+
+ def _malloc_out_of_nursery(self, totalsize):
+ obj = self.sharedarea_tls.malloc_object(totalsize)
+ self.sharedarea_tls.add_regular(obj)
+ return obj
+
+ def collect_roots_from_tldict(self):
+ pass # XXX
+
+ def collect_flush_pending(self):
+ # Follow the objects in the 'pending' stack and move the
+ # young objects they point to out of the nursery.
+ while self.pending.non_empty():
+ obj = self.pending.pop()
+ self.gc.trace(obj, self._trace_drag_out, None)
+
+ def mass_free_old_local(self, previous_sharedarea_tls):
+ obj = previous_sharedarea_tls.chained_list
+ previous_sharedarea_tls.delete()
+ while obj != NULL:
+ hdr = self.gc.header(obj)
+ next = hdr.version
+ if hdr.tid & GCFLAG_VISITED:
+ # survives: relink in the new sharedarea_tls
+ self.sharedarea_tls.add_regular(obj)
+ else:
+ # dies
+ self.sharedarea_tls.free_object(obj)
+ #
+ obj = next
diff --git a/pypy/rpython/memory/gc/test/test_stmtls.py b/pypy/rpython/memory/gc/test/test_stmtls.py
--- a/pypy/rpython/memory/gc/test/test_stmtls.py
+++ b/pypy/rpython/memory/gc/test/test_stmtls.py
@@ -1,10 +1,12 @@
import py
-from pypy.rpython.lltypesystem import lltype, llmemory, llarena, llgroup, rffi
+from pypy.rpython.lltypesystem import lltype, llmemory, llarena, llgroup
from pypy.rpython.memory.gc.stmtls import StmGCTLS, WORD
from pypy.rpython.memory.gc.test.test_stmgc import StmGCTests
from pypy.rpython.memory.support import get_address_stack, get_address_deque
+from pypy.rpython.memory.gcheader import GCHeaderBuilder
+NULL = llmemory.NULL
S = lltype.GcStruct('S', ('a', lltype.Signed), ('b', lltype.Signed),
('c', lltype.Signed))
@@ -18,6 +20,24 @@
class FakeSharedArea:
pass
+class FakeRootWalker:
+ def walk_roots(self, f1, f2, f3, arg):
+ if f1 is not None:
+ A = lltype.Array(llmemory.Address)
+ roots = lltype.malloc(A, len(self.current_stack), flavor='raw')
+ for i in range(len(self.current_stack)):
+ roots[i] = llmemory.cast_ptr_to_adr(self.current_stack[i])
+ for i in range(len(self.current_stack)):
+ root = lltype.direct_ptradd(lltype.direct_arrayitems(roots), i)
+ root = llmemory.cast_ptr_to_adr(root)
+ f1(arg, root)
+ for i in range(len(self.current_stack)):
+ P = lltype.typeOf(self.current_stack[i])
+ self.current_stack[i] = llmemory.cast_adr_to_ptr(roots[i], P)
+ lltype.free(roots, flavor='raw')
+ assert f2 is None
+ assert f3 is None
+
class FakeGC:
from pypy.rpython.memory.support import AddressDict, null_address_dict
AddressStack = get_address_stack()
@@ -25,6 +45,28 @@
nursery_size = 128
stm_operations = FakeStmOperations()
sharedarea = FakeSharedArea()
+ root_walker = FakeRootWalker()
+ HDR = lltype.Struct('header', ('tid', lltype.Signed),
+ ('version', llmemory.Address))
+ gcheaderbuilder = GCHeaderBuilder(HDR)
+
+ def header(self, addr):
+ addr -= self.gcheaderbuilder.size_gc_header
+ return llmemory.cast_adr_to_ptr(addr, lltype.Ptr(self.HDR))
+
+ def get_size(self, addr):
+ return llmemory.sizeof(lltype.typeOf(addr.ptr).TO)
+
+ def trace(self, obj, callback, arg):
+ TYPE = obj.ptr._TYPE.TO
+ if TYPE == S:
+ ofslist = [] # no pointers in S
+ else:
+ assert 0
+ for ofs in ofslist:
+ addr = obj + ofs
+ if addr.address[0]:
+ callback(addr, arg)
class TestStmGCTLS(object):
@@ -36,6 +78,8 @@
self.gctls_main = StmGCTLS(self.gc, in_main_thread=True)
self.gctls_thrd = StmGCTLS(self.gc, in_main_thread=False)
self.gc.main_thread_tls = self.gctls_main
+ self.gctls_main.start_transaction()
+ self.gc.root_walker.current_stack = self.current_stack
def stack_add(self, p):
self.current_stack.append(p)
@@ -43,6 +87,20 @@
def stack_pop(self):
return self.current_stack.pop()
+ def malloc(self, STRUCT):
+ size = llarena.round_up_for_allocation(llmemory.sizeof(STRUCT))
+ size_gc_header = self.gc.gcheaderbuilder.size_gc_header
+ totalsize = size_gc_header + size
+ tls = self.gc.main_thread_tls
+ adr = tls.allocate_bump_pointer(totalsize)
+ #
+ llarena.arena_reserve(adr, totalsize)
+ obj = adr + size_gc_header
+ hdr = self.gc.header(obj)
+ hdr.tid = 0
+ hdr.version = NULL
+ return llmemory.cast_adr_to_ptr(obj, lltype.Ptr(STRUCT))
+
# ----------
def test_creation_works(self):
@@ -59,16 +117,19 @@
assert a6 - a5 == 5
def test_local_collection(self):
- s1, _ = self.malloc(S); s1.a = 111
- s2, _ = self.malloc(S); s2.a = 222
+ s1 = self.malloc(S); s1.a = 111
+ s2 = self.malloc(S); s2.a = 222
self.stack_add(s2)
self.gc.main_thread_tls.local_collection()
s3 = self.stack_pop()
assert s3.a == 222
- xxxx # raises...
- s1.a
- s2.a
+ py.test.raises(RuntimeError, "s1.a")
+ py.test.raises(RuntimeError, "s2.a")
- def test_alloc_a_lot(self):
- for i in range(1000):
- sr1, sr1_adr = self.malloc(SR)
+ def test_alloc_a_lot_nonkept(self):
+ for i in range(100):
+ self.malloc(S)
+
+ def test_alloc_a_lot_kept(self):
+ for i in range(100):
+ self.stack_add(self.malloc(S))
diff --git a/pypy/rpython/memory/gctransform/framework.py b/pypy/rpython/memory/gctransform/framework.py
--- a/pypy/rpython/memory/gctransform/framework.py
+++ b/pypy/rpython/memory/gctransform/framework.py
@@ -1374,16 +1374,19 @@
def walk_roots(self, collect_stack_root,
collect_static_in_prebuilt_nongc,
- collect_static_in_prebuilt_gc):
+ collect_static_in_prebuilt_gc,
+ arg=None):
gcdata = self.gcdata
gc = self.gc
+ if arg is None:
+ arg = gc
if collect_static_in_prebuilt_nongc:
addr = gcdata.static_root_start
end = gcdata.static_root_nongcend
while addr != end:
result = addr.address[0]
if gc.points_to_valid_gc_object(result):
- collect_static_in_prebuilt_nongc(gc, result)
+ collect_static_in_prebuilt_nongc(arg, result)
addr += sizeofaddr
if collect_static_in_prebuilt_gc:
addr = gcdata.static_root_nongcend
@@ -1391,10 +1394,10 @@
while addr != end:
result = addr.address[0]
if gc.points_to_valid_gc_object(result):
- collect_static_in_prebuilt_gc(gc, result)
+ collect_static_in_prebuilt_gc(arg, result)
addr += sizeofaddr
if collect_stack_root:
- self.walk_stack_roots(collect_stack_root) # abstract
+ self.walk_stack_roots(collect_stack_root, arg) # abstract
def need_stacklet_support(self):
raise Exception("%s does not support stacklets" % (
diff --git a/pypy/rpython/memory/gctransform/shadowstack.py b/pypy/rpython/memory/gctransform/shadowstack.py
--- a/pypy/rpython/memory/gctransform/shadowstack.py
+++ b/pypy/rpython/memory/gctransform/shadowstack.py
@@ -28,7 +28,7 @@
self.decr_stack = decr_stack
root_iterator = get_root_iterator(gctransformer)
- def walk_stack_root(callback, start, end):
+ def walk_stack_root(callback, arg, start, end):
root_iterator.setcontext(NonConstant(llmemory.NULL))
gc = self.gc
addr = end
@@ -36,7 +36,7 @@
addr = root_iterator.nextleft(gc, start, addr)
if addr == llmemory.NULL:
return
- callback(gc, addr)
+ callback(arg, addr)
self.rootstackhook = walk_stack_root
self.shadow_stack_pool = ShadowStackPool(gcdata)
@@ -56,9 +56,9 @@
self.shadow_stack_pool.initial_setup()
BaseRootWalker.setup_root_walker(self)
- def walk_stack_roots(self, collect_stack_root):
+ def walk_stack_roots(self, collect_stack_root, arg):
gcdata = self.gcdata
- self.rootstackhook(collect_stack_root,
+ self.rootstackhook(collect_stack_root, arg,
gcdata.root_stack_base, gcdata.root_stack_top)
def need_thread_support(self, gctransformer, getfn):
More information about the pypy-commit
mailing list