[pypy-commit] pypy gc-forkfriendly: Actually store flags separately from the GC header, when leaving the nursery.
devin.jeanpierre
pypy.commits at gmail.com
Wed May 18 00:19:20 EDT 2016
Author: Devin Jeanpierre <jeanpierreda at gmail.com>
Branch: gc-forkfriendly
Changeset: r84511:362654e05ac5
Date: 2016-05-17 21:18 -0700
http://bitbucket.org/pypy/pypy/changeset/362654e05ac5/
Log: Actually store flags separately from the GC header, when leaving the
nursery.
diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py
--- a/rpython/memory/gc/incminimark.py
+++ b/rpython/memory/gc/incminimark.py
@@ -155,7 +155,11 @@
# 'old_objects_pointing_to_pinned' and doesn't have to be added again.
GCFLAG_PINNED_OBJECT_PARENT_KNOWN = GCFLAG_PINNED
-_GCFLAG_FIRST_UNUSED = first_gcflag << 10 # the first unused bit
+# The object is dead or will be dead soon. (Only useful with separate headers
+# that might outlive the object, see incminimark_remoteheader.py)
+GCFLAG_DEAD = first_gcflag << 10
+
+_GCFLAG_FIRST_UNUSED = first_gcflag << 11 # the first unused bit
# States for the incremental GC
@@ -2243,8 +2247,7 @@
if self.old_objects_with_light_finalizers.non_empty():
self.deal_with_old_objects_with_finalizers()
# objects_to_trace processed fully, can move on to sweeping
- self.ac.mass_free_prepare()
- self.start_free_rawmalloc_objects()
+ self.start_free()
#
# get rid of objects pointing to pinned objects that were not
# visited
@@ -2280,8 +2283,7 @@
# GCFLAG_VISITED on the others. Visit at most '3 *
# nursery_size' bytes.
limit = 3 * self.nursery_size // self.ac.page_size
- done = self.ac.mass_free_incremental(self._free_if_unvisited,
- limit)
+ done = self.free_unvisited_arena_objects_step(limit)
# XXX tweak the limits above
#
if done:
@@ -2348,11 +2350,16 @@
if self.get_flags(obj) & GCFLAG_VISITED:
self.remove_flags(obj, GCFLAG_VISITED)
return False # survives
- return True # dies
+ # dies
+ self.finalize_header(hdr)
+ return True
def _reset_gcflag_visited(self, obj, ignored):
self.remove_flags(obj, GCFLAG_VISITED)
+ def free_unvisited_arena_objects_step(self, limit):
+ return self.ac.mass_free_incremental(self._free_if_unvisited, limit)
+
def free_rawmalloced_object_if_unvisited(self, obj, check_flag):
if self.get_flags(obj) & check_flag:
self.remove_flags(obj, check_flag) # survives
@@ -2380,6 +2387,10 @@
llarena.arena_free(arena)
self.rawmalloced_total_size -= r_uint(allocsize)
+ def start_free(self):
+ self.ac.mass_free_prepare()
+ self.start_free_rawmalloc_objects()
+
def start_free_rawmalloc_objects(self):
ll_assert(not self.raw_malloc_might_sweep.non_empty(),
"raw_malloc_might_sweep must be empty")
@@ -3036,6 +3047,9 @@
# Methods meant to be overridden by subclasses that store flags elsewhere.
+ def finalize_header(self, hdr):
+ """Clean up hdr before the object is freed."""
+
def copy_header(self, src, dest):
self.header(dest).tid = self.header(src).tid
diff --git a/rpython/memory/gc/incminimark_remoteheader.py b/rpython/memory/gc/incminimark_remoteheader.py
--- a/rpython/memory/gc/incminimark_remoteheader.py
+++ b/rpython/memory/gc/incminimark_remoteheader.py
@@ -1,19 +1,35 @@
"""Incminimark with GC flags stored in a separate page for fork-friendliness."""
+from rpython.rtyper.lltypesystem import llarena
from rpython.memory.gc import incminimark
-from rpython.rtyper.lltypesystem import lltype, llmemory
+from rpython.rlib.rarithmetic import LONG_BIT
+from rpython.rtyper.lltypesystem import rffi, lltype, llmemory
+
+SIGNEDP = lltype.Ptr(lltype.FixedSizeArray(lltype.Signed, 1))
class IncrementalMiniMarkRemoteHeaderGC(incminimark.IncrementalMiniMarkGC):
# The GC header is similar to incminimark, except that the flags can be
# placed anywhere, not just in the bits of tid.
- # TODO: Actually place flags somewhere other than tid.
HDR = lltype.Struct('header',
('tid', lltype.Signed),
- ('remote_flags', lltype.Ptr(lltype.FixedSizeArray(lltype.Signed, 1))))
+ ('remote_flags', SIGNEDP))
- def init_gc_object(self, addr, typeid16, flags=0):
- super(IncrementalMiniMarkRemoteHeaderGC, self).init_gc_object(addr, typeid16, flags)
- hdr = llmemory.cast_adr_to_ptr(addr, lltype.Ptr(self.HDR))
+ def __init__(self, config, **kwargs):
+ super(IncrementalMiniMarkRemoteHeaderGC, self).__init__(config, **kwargs)
+ ArenaCollectionClass = kwargs.get('ArenaCollectionClass', None)
+ if ArenaCollectionClass is None:
+ from rpython.memory.gc import minimarkpage
+ ArenaCollectionClass = minimarkpage.ArenaCollection
+
+ # TODO: can I reuse self.ac somehow? Is there a better thing to use?
+ # This seems absurd.
+ self.__ac_for_flags = ArenaCollectionClass(
+ 64*incminimark.WORD, 16*incminimark.WORD,
+ small_request_threshold=LONG_BIT)
+
+ def init_gc_object(self, adr, typeid16, flags=0):
+ super(IncrementalMiniMarkRemoteHeaderGC, self).init_gc_object(adr, typeid16, flags)
+ hdr = llmemory.cast_adr_to_ptr(adr, lltype.Ptr(self.HDR))
hdr.remote_flags = lltype.direct_fieldptr(hdr, 'tid')
def make_forwardstub(self, obj, forward_to):
@@ -28,7 +44,42 @@
dest_hdr = self.header(dest)
dest_hdr.tid = self.get_flags(src)
dest_hdr.remote_flags = lltype.direct_fieldptr(dest_hdr, 'tid')
- # TODO: make new remote flag sometimes.
+ self.__extract_flags_to_pointer(dest_hdr)
+
+ def __extract_flags_to_pointer(self, hdr):
+ """Make an object's GC header use out-of-line flags.
+
+ Expects the object to not use inline tid-flags.
+ """
+ assert (hdr.remote_flags == lltype.nullptr(SIGNEDP.TO)
+ or hdr.remote_flags == lltype.direct_fieldptr(hdr, 'tid')), \
+ "leaking old remote_flags!"
+ size = llmemory.sizeof(lltype.Signed)
+ adr = self.__ac_for_flags.malloc(size)
+ hdr.remote_flags = llmemory.cast_adr_to_ptr(adr, SIGNEDP)
+ hdr.remote_flags[0] = hdr.tid
+
+ def finalize_header(self, adr):
+ hdr = llmemory.cast_adr_to_ptr(adr, lltype.Ptr(self.HDR))
+ if hdr.remote_flags != lltype.nullptr(SIGNEDP.TO):
+ # If it points to allocated memory, this will be picked up by
+ # __free_flags_if_finalized.
+ hdr.remote_flags[0] |= incminimark.GCFLAG_DEAD
+
+ def __free_flags_if_finalized(self, adr):
+ flag_ptr = llmemory.cast_adr_to_ptr(adr, SIGNEDP)
+ # If -42, it was set in finalize_header and the object was freed.
+ return flag_ptr[0] & incminimark.GCFLAG_DEAD
+
+ def free_unvisited_arena_objects_step(self, limit):
+ done = super(IncrementalMiniMarkRemoteHeaderGC, self).free_unvisited_arena_objects_step(limit)
+ self.__ac_for_flags.mass_free_incremental(
+ self.__free_flags_if_finalized, done)
+ return done
+
+ def start_free(self):
+ super(IncrementalMiniMarkRemoteHeaderGC, self).start_free()
+ self.__ac_for_flags.mass_free_prepare()
# Manipulate flags through a pointer.
diff --git a/rpython/memory/gc/test/test_direct.py b/rpython/memory/gc/test/test_direct.py
--- a/rpython/memory/gc/test/test_direct.py
+++ b/rpython/memory/gc/test/test_direct.py
@@ -537,26 +537,24 @@
#
addr_src = llmemory.cast_ptr_to_adr(p_src)
addr_dst = llmemory.cast_ptr_to_adr(p_dst)
- hdr_src = self.gc.header(addr_src)
- hdr_dst = self.gc.header(addr_dst)
#
- assert hdr_src.tid & minimark.GCFLAG_TRACK_YOUNG_PTRS
- assert hdr_dst.tid & minimark.GCFLAG_TRACK_YOUNG_PTRS
+ assert self.gc.get_flags(addr_src) & minimark.GCFLAG_TRACK_YOUNG_PTRS
+ assert self.gc.get_flags(addr_dst) & minimark.GCFLAG_TRACK_YOUNG_PTRS
#
res = self.gc.writebarrier_before_copy(addr_src, addr_dst, 0, 0, 10)
assert res
- assert hdr_dst.tid & minimark.GCFLAG_TRACK_YOUNG_PTRS
+ assert self.gc.get_flags(addr_dst) & minimark.GCFLAG_TRACK_YOUNG_PTRS
#
- hdr_src.tid &= ~minimark.GCFLAG_TRACK_YOUNG_PTRS # pretend we have young ptrs
+ self.gc.remove_flags(addr_src, minimark.GCFLAG_TRACK_YOUNG_PTRS) # pretend we have young ptrs
res = self.gc.writebarrier_before_copy(addr_src, addr_dst, 0, 0, 10)
assert res # we optimized it
- assert hdr_dst.tid & minimark.GCFLAG_TRACK_YOUNG_PTRS == 0 # and we copied the flag
+ assert self.gc.get_flags(addr_dst) & minimark.GCFLAG_TRACK_YOUNG_PTRS == 0 # and we copied the flag
#
- hdr_src.tid |= minimark.GCFLAG_TRACK_YOUNG_PTRS
- hdr_dst.tid |= minimark.GCFLAG_TRACK_YOUNG_PTRS
- hdr_src.tid |= minimark.GCFLAG_HAS_CARDS
- hdr_src.tid |= minimark.GCFLAG_CARDS_SET
- # hdr_dst.tid does not have minimark.GCFLAG_HAS_CARDS
+ self.gc.add_flags(addr_src, minimark.GCFLAG_TRACK_YOUNG_PTRS)
+ self.gc.add_flags(addr_dst, minimark.GCFLAG_TRACK_YOUNG_PTRS)
+ self.gc.add_flags(addr_src, minimark.GCFLAG_HAS_CARDS)
+ self.gc.add_flags(addr_src, minimark.GCFLAG_CARDS_SET)
+ # addr_dst flags don't not have minimark.GCFLAG_HAS_CARDS
res = self.gc.writebarrier_before_copy(addr_src, addr_dst, 0, 0, 10)
assert not res # there might be young ptrs, let ll_arraycopy to find them
@@ -567,18 +565,16 @@
self.gc.next_major_collection_threshold = 99999.0
addr_src = self.gc.external_malloc(tid, largeobj_size, alloc_young=True)
addr_dst = self.gc.external_malloc(tid, largeobj_size, alloc_young=True)
- hdr_src = self.gc.header(addr_src)
- hdr_dst = self.gc.header(addr_dst)
#
- assert hdr_src.tid & minimark.GCFLAG_HAS_CARDS
- assert hdr_dst.tid & minimark.GCFLAG_HAS_CARDS
+ assert self.gc.get_flags(addr_src) & minimark.GCFLAG_HAS_CARDS
+ assert self.gc.get_flags(addr_dst) & minimark.GCFLAG_HAS_CARDS
#
self.gc.write_barrier_from_array(addr_src, 0)
index_in_third_page = int(2.5 * self.gc.card_page_indices)
assert index_in_third_page < largeobj_size
self.gc.write_barrier_from_array(addr_src, index_in_third_page)
#
- assert hdr_src.tid & minimark.GCFLAG_CARDS_SET
+ assert self.gc.get_flags(addr_src) & minimark.GCFLAG_CARDS_SET
addr_byte = self.gc.get_card(addr_src, 0)
assert ord(addr_byte.char[0]) == 0x01 | 0x04 # bits 0 and 2
#
@@ -586,7 +582,7 @@
0, 0, 2*self.gc.card_page_indices)
assert res
#
- assert hdr_dst.tid & minimark.GCFLAG_CARDS_SET
+ assert self.gc.get_flags(addr_dst) & minimark.GCFLAG_CARDS_SET
addr_byte = self.gc.get_card(addr_dst, 0)
assert ord(addr_byte.char[0]) == 0x01 | 0x04 # bits 0 and 2
@@ -607,20 +603,20 @@
self.stackroots.append(curobj)
- oldobj = self.stackroots[-1]
- oldhdr = self.gc.header(llmemory.cast_ptr_to_adr(oldobj))
+ oldadr = llmemory.cast_ptr_to_adr(self.stackroots[-1])
- assert oldhdr.tid & incminimark.GCFLAG_VISITED == 0
+ assert self.gc.get_flags(oldadr) & incminimark.GCFLAG_VISITED == 0
self.gc.debug_gc_step_until(incminimark.STATE_MARKING)
oldobj = self.stackroots[-1]
+ oldadr = llmemory.cast_ptr_to_adr(oldobj)
# object shifted by minor collect
- oldhdr = self.gc.header(llmemory.cast_ptr_to_adr(oldobj))
- assert oldhdr.tid & incminimark.GCFLAG_VISITED == 0
+ oldhdr = self.gc.header(oldadr)
+ assert self.gc.get_flags(oldadr) & incminimark.GCFLAG_VISITED == 0
self.gc._minor_collection()
self.gc.visit_all_objects_step(1)
- assert oldhdr.tid & incminimark.GCFLAG_VISITED
+ assert self.gc.get_flags(oldadr) & incminimark.GCFLAG_VISITED
#at this point the first object should have been processed
newobj = self.malloc(S)
@@ -641,8 +637,8 @@
self.gc.debug_gc_step_until(incminimark.STATE_SWEEPING)
oldobj = self.stackroots[-1]
- oldhdr = self.gc.header(llmemory.cast_ptr_to_adr(oldobj))
- assert oldhdr.tid & incminimark.GCFLAG_VISITED
+ oldadr = llmemory.cast_ptr_to_adr(oldobj)
+ assert self.gc.get_flags(oldadr) & incminimark.GCFLAG_VISITED
newobj1 = self.malloc(S)
newobj2 = self.malloc(S)
More information about the pypy-commit
mailing list