Author: Stefan Beyer <[email protected]>
Branch: cpyext-gc-cycle
Changeset: r97191:13bf4458e03a
Date: 2019-08-15 14:28 +0200
http://bitbucket.org/pypy/pypy/changeset/13bf4458e03a/
Log: Adapted incremental rrc to use snapshot
diff --git a/rpython/memory/gc/rrc/base.py b/rpython/memory/gc/rrc/base.py
--- a/rpython/memory/gc/rrc/base.py
+++ b/rpython/memory/gc/rrc/base.py
@@ -40,7 +40,7 @@
_ADDRARRAY = lltype.Array(llmemory.Address, hints={'nolength': True})
PYOBJ_SNAPSHOT_OBJ = lltype.Struct('PyObject_Snapshot',
('pyobj', llmemory.Address),
- ('refcnt', lltype.Signed),
+ ('status', lltype.Signed),
('refcnt_external', lltype.Signed),
('refs_index', lltype.Signed),
('refs_len', lltype.Signed),
@@ -442,7 +442,6 @@
else:
self._free(pyobject, True)
-
def _untrack_tuples(self):
gchdr = self.tuple_list.c_gc_next
while gchdr <> self.tuple_list:
@@ -456,136 +455,7 @@
self._gc_list_add(self.pyobj_list, gchdr)
gchdr = gchdr_next
- def _collect_roots(self, pygclist):
- # Initialize the cyclic refcount with the real refcount.
- self._collect_roots_init_list(pygclist)
-
- # Save the real refcount of objects at border
- self.p_list_old.foreach(self._obj_save_refcnt, None)
- self.o_list_old.foreach(self._obj_save_refcnt, None)
-
- # Subtract all internal refcounts from the cyclic refcount
- # of rawrefcounted objects
- self._collect_roots_subtract_internal(pygclist)
-
- # For all non-gc pyobjects which have a refcount > 0,
- # mark all reachable objects on the pypy side
- self.p_list_old.foreach(self._major_trace_nongc, True)
-
- # For every object in this set, if it is marked, add 1 as a real
- # refcount (p_list => pyobj stays alive if obj stays alive).
- self.p_list_old.foreach(self._obj_fix_refcnt, None)
- self.o_list_old.foreach(self._obj_fix_refcnt, None)
-
- # now all rawrefcounted roots or live border objects have a
- # refcount > 0
- self._debug_check_consistency(print_label="rc-initialized")
-
- def _collect_roots_init_list(self, pygclist):
- from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
- from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
- pygchdr = pygclist.c_gc_next
- while pygchdr <> pygclist:
- refcnt = self.gc_as_pyobj(pygchdr).c_ob_refcnt
- if refcnt >= REFCNT_FROM_PYPY_LIGHT:
- refcnt -= REFCNT_FROM_PYPY_LIGHT
- elif refcnt >= REFCNT_FROM_PYPY:
- refcnt -= REFCNT_FROM_PYPY
- self._pyobj_gc_refcnt_set(pygchdr, refcnt)
- pygchdr = pygchdr.c_gc_next
-
- def _collect_roots_subtract_internal(self, pygclist):
- pygchdr = pygclist.c_gc_next
- while pygchdr <> pygclist:
- pyobj = self.gc_as_pyobj(pygchdr)
- self._traverse(pyobj, -1)
- pygchdr = pygchdr.c_gc_next
-
- def _pyobj_gc_refcnt_set(self, pygchdr, refcnt):
- pygchdr.c_gc_refs &= self.RAWREFCOUNT_REFS_MASK_FINALIZED
- pygchdr.c_gc_refs |= refcnt << self.RAWREFCOUNT_REFS_SHIFT
-
- def _obj_save_refcnt(self, pyobject, ignore):
- pyobj = self._pyobj(pyobject)
- link = llmemory.cast_int_to_adr(pyobj.c_ob_pypy_link)
- self.refcnt_dict.setitem(pyobject, link)
- pyobj.c_ob_pypy_link = pyobj.c_ob_refcnt
-
- def _obj_fix_refcnt(self, pyobject, ignore):
- pyobj = self._pyobj(pyobject)
- #intobj = pyobj.c_ob_pypy_link
- #obj = llmemory.cast_int_to_adr(intobj)
- obj = self.refcnt_dict.get(pyobject)
- gchdr = self.pyobj_as_gc(pyobj)
- if gchdr <> lltype.nullptr(self.PYOBJ_GC_HDR):
- rc = gchdr.c_gc_refs
- refcnt = gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT
- if rc == self.RAWREFCOUNT_REFS_UNTRACKED:
- debug_print("gc obj not tracked", gchdr, ": obj", obj,
- "cyclic-rc", rc)
- else:
- debug_print("gc obj tracked", gchdr, ": obj", obj, "real-rc",
- refcnt, "gc-next",
- gchdr.c_gc_next, "gc-prev", gchdr.c_gc_prev)
- if self.gc.header(obj).tid & (self.GCFLAG_VISITED |
- self.GCFLAG_NO_HEAP_PTRS):
- refcnt += 1
- self._pyobj_gc_refcnt_set(gchdr, refcnt)
-
- def _mark_rawrefcount(self):
- if self._gc_list_is_empty(self.pyobj_list):
- self._gc_list_init(self.pyobj_old_list)
- else:
- self._gc_list_move(self.pyobj_list, self.pyobj_old_list)
- # as long as new objects with cyclic a refcount > 0 or alive border
- # objects are found, increment the refcount of all referenced objects
- # of those newly found objects
- found_alive = True
- pyobj_old = self.pyobj_list
- #
- while found_alive: # TODO: working set to improve performance?
- found_alive = False
- gchdr = self.pyobj_old_list.c_gc_next
- while gchdr <> self.pyobj_old_list:
- next_old = gchdr.c_gc_next
- found_alive |= self._mark_rawrefcount_obj(gchdr, pyobj_old)
- gchdr = next_old
- #
- # now all rawrefcounted objects, which are alive, have a cyclic
- # refcount > 0 or are marked
-
- def _mark_rawrefcount_obj(self, gchdr, gchdr_move):
- alive = (gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT) > 0
- pyobj = self.gc_as_pyobj(gchdr)
- obj = llmemory.NULL
- if pyobj.c_ob_pypy_link <> 0:
- #intobj = pyobj.c_ob_pypy_link
- #obj = llmemory.cast_int_to_adr(intobj)
- pyobject = llmemory.cast_ptr_to_adr(pyobj)
- obj = self.refcnt_dict.get(pyobject)
- if not alive and self.gc.header(obj).tid & (
- self.GCFLAG_VISITED | self.GCFLAG_NO_HEAP_PTRS):
- # add fake refcount, to mark it as live
- gchdr.c_gc_refs += 1 << self.RAWREFCOUNT_REFS_SHIFT
- alive = True
- if alive:
- # remove from old list
- next = gchdr.c_gc_next
- next.c_gc_prev = gchdr.c_gc_prev
- gchdr.c_gc_prev.c_gc_next = next
- # add to new list (or not, if it is a tuple)
- self._gc_list_add(gchdr_move, gchdr)
- # increment refcounts
- self._traverse(pyobj, 1)
- # mark recursively, if it is a pypyobj
- if pyobj.c_ob_pypy_link <> 0:
- #intobj = pyobj.c_ob_pypy_link
- #obj = llmemory.cast_int_to_adr(intobj)
- self.gc.objects_to_trace.append(obj)
- self.gc.visit_all_objects()
- return alive
-
- def _find_garbage(self):
+ def _find_garbage(self, use_dict):
found_garbage = False
gchdr = self.pyobj_old_list.c_gc_next
while gchdr <> self.pyobj_old_list:
@@ -593,12 +463,12 @@
garbage = self.finalizer_type(gchdr) == \
self.RAWREFCOUNT_FINALIZER_LEGACY
if garbage:
- self._move_to_garbage(gchdr)
+ self._move_to_garbage(gchdr, use_dict)
found_garbage = True
gchdr = next_old
return found_garbage
- def _mark_garbage(self):
+ def _mark_garbage(self, use_dict):
found_garbage = True
#
while found_garbage:
@@ -609,21 +479,23 @@
alive = (gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT) > 0
pyobj = self.gc_as_pyobj(gchdr)
if pyobj.c_ob_pypy_link <> 0:
- #intobj = pyobj.c_ob_pypy_link
- #obj = llmemory.cast_int_to_adr(intobj)
- pyobject = llmemory.cast_ptr_to_adr(pyobj)
- obj = self.refcnt_dict.get(pyobject)
+ if use_dict:
+ pyobject = llmemory.cast_ptr_to_adr(pyobj)
+ obj = self.refcnt_dict.get(pyobject)
+ else:
+ intobj = pyobj.c_ob_pypy_link
+ obj = llmemory.cast_int_to_adr(intobj)
if not alive and self.gc.header(obj).tid & (
self.GCFLAG_VISITED | self.GCFLAG_NO_HEAP_PTRS):
# add fake refcount, to mark it as live
gchdr.c_gc_refs += 1 << self.RAWREFCOUNT_REFS_SHIFT
alive = True
if alive:
- self._move_to_garbage(gchdr)
+ self._move_to_garbage(gchdr, use_dict)
found_garbage = True
gchdr = next_old
- def _move_to_garbage(self, gchdr):
+ def _move_to_garbage(self, gchdr, use_dict):
pyobj = self.gc_as_pyobj(gchdr)
# remove from old list
next = gchdr.c_gc_next
@@ -642,10 +514,12 @@
# refcount of zero or an unmarked linked pypy object)
self._traverse(pyobj, 1)
if pyobj.c_ob_pypy_link <> 0:
- #intobj = pyobj.c_ob_pypy_link
- pyobject = llmemory.cast_ptr_to_adr(pyobj)
- obj = self.refcnt_dict.get(pyobject)
- #obj = llmemory.cast_int_to_adr(intobj)
+ if use_dict:
+ pyobject = llmemory.cast_ptr_to_adr(pyobj)
+ obj = self.refcnt_dict.get(pyobject)
+ else:
+ intobj = pyobj.c_ob_pypy_link
+ obj = llmemory.cast_int_to_adr(intobj)
self.garbage_to_trace.append(obj)
self.gc.objects_to_trace.append(obj)
self.gc.visit_all_objects()
diff --git a/rpython/memory/gc/rrc/incmark.py b/rpython/memory/gc/rrc/incmark.py
--- a/rpython/memory/gc/rrc/incmark.py
+++ b/rpython/memory/gc/rrc/incmark.py
@@ -55,12 +55,6 @@
self.state = self.STATE_GARBAGE_MARKING
return False
- elif self.state == self.STATE_GARBAGE_MARKING:
- #if self._find_garbage(): # handle legacy finalizers # TODO: from
snapshot
- # self._mark_garbage() # TODO: from snapshot
- # self._debug_check_consistency(print_label="end-legacy-fin")
- self.state = self.STATE_DEFAULT
-
# now move all dead objs still in pyob_list to garbage
# dead -> pyobj_old_list
# live -> set cyclic refcount to > 0
@@ -81,6 +75,11 @@
pygchdr.c_gc_refs = 1 # new object, keep alive
pygchdr = next_old
+ if self._find_garbage(False): # handle legacy finalizers
+ self._mark_garbage(False)
+ self._debug_check_consistency(print_label="end-legacy-fin")
+ self.state = self.STATE_DEFAULT
+
# We are finished with marking, now finish things up
found_finalizer = self._find_finalizer() # modern finalizers
if found_finalizer:
@@ -127,7 +126,7 @@
# refcount > 0 or are marked
def _mark_rawrefcount_obj(self, snapobj):
- if snapobj.refcnt == 0: # hack
+ if snapobj.status == 0:
return False
alive = snapobj.refcnt_external > 0
@@ -149,9 +148,8 @@
if snapobj.pypy_link <> 0:
self.gc.objects_to_trace.append(obj)
self.gc.visit_all_objects()
-
- # remove from old list, TODO: hack -> working set might be better
- snapobj.refcnt = 0
+ # mark as processed
+ snapobj.status = 0
return alive
def _take_snapshot(self, pygclist):
@@ -196,7 +194,7 @@
pygchdr.c_gc_refs = objs_index + 1
obj = self.snapshot_objs[objs_index]
obj.pyobj = llmemory.cast_ptr_to_adr(pyobj)
- obj.refcnt = 1
+ obj.status = 1
obj.refcnt_external = refcnt
obj.refs_index = refs_index
obj.refs_len = 0
diff --git a/rpython/memory/gc/rrc/mark.py b/rpython/memory/gc/rrc/mark.py
--- a/rpython/memory/gc/rrc/mark.py
+++ b/rpython/memory/gc/rrc/mark.py
@@ -1,4 +1,5 @@
from rpython.memory.gc.rrc.base import RawRefCountBaseGC
+from rpython.rtyper.lltypesystem import lltype, llmemory, llgroup, rffi
from rpython.rlib.debug import ll_assert, debug_print, debug_start, debug_stop
class RawRefCountMarkGC(RawRefCountBaseGC):
@@ -35,8 +36,8 @@
self._mark_rawrefcount()
self._debug_check_consistency(print_label="before-fin")
self.state = self.STATE_GARBAGE_MARKING
- if self._find_garbage(): # handle legacy finalizers
- self._mark_garbage()
+ if self._find_garbage(True): # handle legacy finalizers
+ self._mark_garbage(True)
self._debug_check_consistency(print_label="end-legacy-fin")
self.state = self.STATE_MARKING
found_finalizer = self._find_finalizer()
@@ -60,3 +61,132 @@
self.state = self.STATE_DEFAULT
return True
+
+ def _collect_roots(self, pygclist):
+ # Initialize the cyclic refcount with the real refcount.
+ self._collect_roots_init_list(pygclist)
+
+ # Save the real refcount of objects at border
+ self.p_list_old.foreach(self._obj_save_refcnt, None)
+ self.o_list_old.foreach(self._obj_save_refcnt, None)
+
+ # Subtract all internal refcounts from the cyclic refcount
+ # of rawrefcounted objects
+ self._collect_roots_subtract_internal(pygclist)
+
+ # For all non-gc pyobjects which have a refcount > 0,
+ # mark all reachable objects on the pypy side
+ self.p_list_old.foreach(self._major_trace_nongc, True)
+
+ # For every object in this set, if it is marked, add 1 as a real
+ # refcount (p_list => pyobj stays alive if obj stays alive).
+ self.p_list_old.foreach(self._obj_fix_refcnt, None)
+ self.o_list_old.foreach(self._obj_fix_refcnt, None)
+
+ # now all rawrefcounted roots or live border objects have a
+ # refcount > 0
+ self._debug_check_consistency(print_label="rc-initialized")
+
+ def _collect_roots_init_list(self, pygclist):
+ from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
+ from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
+ pygchdr = pygclist.c_gc_next
+ while pygchdr <> pygclist:
+ refcnt = self.gc_as_pyobj(pygchdr).c_ob_refcnt
+ if refcnt >= REFCNT_FROM_PYPY_LIGHT:
+ refcnt -= REFCNT_FROM_PYPY_LIGHT
+ elif refcnt >= REFCNT_FROM_PYPY:
+ refcnt -= REFCNT_FROM_PYPY
+ self._pyobj_gc_refcnt_set(pygchdr, refcnt)
+ pygchdr = pygchdr.c_gc_next
+
+ def _collect_roots_subtract_internal(self, pygclist):
+ pygchdr = pygclist.c_gc_next
+ while pygchdr <> pygclist:
+ pyobj = self.gc_as_pyobj(pygchdr)
+ self._traverse(pyobj, -1)
+ pygchdr = pygchdr.c_gc_next
+
+ def _pyobj_gc_refcnt_set(self, pygchdr, refcnt):
+ pygchdr.c_gc_refs &= self.RAWREFCOUNT_REFS_MASK_FINALIZED
+ pygchdr.c_gc_refs |= refcnt << self.RAWREFCOUNT_REFS_SHIFT
+
+ def _obj_save_refcnt(self, pyobject, ignore):
+ pyobj = self._pyobj(pyobject)
+ link = llmemory.cast_int_to_adr(pyobj.c_ob_pypy_link)
+ self.refcnt_dict.setitem(pyobject, link)
+ pyobj.c_ob_pypy_link = pyobj.c_ob_refcnt
+
+ def _obj_fix_refcnt(self, pyobject, ignore):
+ pyobj = self._pyobj(pyobject)
+ #intobj = pyobj.c_ob_pypy_link
+ #obj = llmemory.cast_int_to_adr(intobj)
+ obj = self.refcnt_dict.get(pyobject)
+ gchdr = self.pyobj_as_gc(pyobj)
+ if gchdr <> lltype.nullptr(self.PYOBJ_GC_HDR):
+ rc = gchdr.c_gc_refs
+ refcnt = gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT
+ if rc == self.RAWREFCOUNT_REFS_UNTRACKED:
+ debug_print("gc obj not tracked", gchdr, ": obj", obj,
+ "cyclic-rc", rc)
+ else:
+ debug_print("gc obj tracked", gchdr, ": obj", obj, "real-rc",
+ refcnt, "gc-next",
+ gchdr.c_gc_next, "gc-prev", gchdr.c_gc_prev)
+ if self.gc.header(obj).tid & (self.GCFLAG_VISITED |
+ self.GCFLAG_NO_HEAP_PTRS):
+ refcnt += 1
+ self._pyobj_gc_refcnt_set(gchdr, refcnt)
+
+ def _mark_rawrefcount(self):
+ if self._gc_list_is_empty(self.pyobj_list):
+ self._gc_list_init(self.pyobj_old_list)
+ else:
+ self._gc_list_move(self.pyobj_list, self.pyobj_old_list)
+ # as long as new objects with cyclic a refcount > 0 or alive border
+ # objects are found, increment the refcount of all referenced objects
+ # of those newly found objects
+ found_alive = True
+ pyobj_old = self.pyobj_list
+ #
+ while found_alive: # TODO: working set to improve performance?
+ found_alive = False
+ gchdr = self.pyobj_old_list.c_gc_next
+ while gchdr <> self.pyobj_old_list:
+ next_old = gchdr.c_gc_next
+ found_alive |= self._mark_rawrefcount_obj(gchdr, pyobj_old)
+ gchdr = next_old
+ #
+ # now all rawrefcounted objects, which are alive, have a cyclic
+ # refcount > 0 or are marked
+
+ def _mark_rawrefcount_obj(self, gchdr, gchdr_move):
+ alive = (gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT) > 0
+ pyobj = self.gc_as_pyobj(gchdr)
+ obj = llmemory.NULL
+ if pyobj.c_ob_pypy_link <> 0:
+ #intobj = pyobj.c_ob_pypy_link
+ #obj = llmemory.cast_int_to_adr(intobj)
+ pyobject = llmemory.cast_ptr_to_adr(pyobj)
+ obj = self.refcnt_dict.get(pyobject)
+ if not alive and self.gc.header(obj).tid & (
+ self.GCFLAG_VISITED | self.GCFLAG_NO_HEAP_PTRS):
+ # add fake refcount, to mark it as live
+ gchdr.c_gc_refs += 1 << self.RAWREFCOUNT_REFS_SHIFT
+ alive = True
+ if alive:
+ # remove from old list
+ next = gchdr.c_gc_next
+ next.c_gc_prev = gchdr.c_gc_prev
+ gchdr.c_gc_prev.c_gc_next = next
+ # add to new list (or not, if it is a tuple)
+ self._gc_list_add(gchdr_move, gchdr)
+ # increment refcounts
+ self._traverse(pyobj, 1)
+ # mark recursively, if it is a pypyobj
+ if pyobj.c_ob_pypy_link <> 0:
+ #intobj = pyobj.c_ob_pypy_link
+ #obj = llmemory.cast_int_to_adr(intobj)
+ self.gc.objects_to_trace.append(obj)
+ self.gc.visit_all_objects()
+ return alive
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit