Author: Stefan Beyer <[email protected]>
Branch: cpyext-gc-cycle
Changeset: r96199:f0fce8ff6d08
Date: 2019-03-02 11:15 +0100
http://bitbucket.org/pypy/pypy/changeset/f0fce8ff6d08/
Log: Implemented additional rawrefcount states and added a gc-header flag
Extended interface
diff --git a/pypy/module/cpyext/state.py b/pypy/module/cpyext/state.py
--- a/pypy/module/cpyext/state.py
+++ b/pypy/module/cpyext/state.py
@@ -113,6 +113,7 @@
if adr_int == llmemory.cast_adr_to_int(
llmemory.cast_ptr_to_adr(head)):
rawrefcount.cyclic_garbage_remove()
+ rawrefcount.begin_garbage()
w_list = space.newlist([])
while True:
w_obj = rawrefcount.next_garbage_pypy(W_Root)
@@ -130,6 +131,7 @@
last_py_obj = w_pyobj
space.setattr(space.builtin_modules['gc'],
space.newtext('garbage'), w_list)
+ rawrefcount.end_garbage()
print 'dealloc_trigger DONE'
return "RETRY"
def tp_traverse(pyobj_ptr, callback, args):
@@ -321,6 +323,7 @@
if adr_int == llmemory.cast_adr_to_int(llmemory.cast_ptr_to_adr(head)):
rawrefcount.cyclic_garbage_remove()
+ rawrefcount.begin_garbage()
w_list = space.newlist([])
while True:
w_obj = rawrefcount.next_garbage_pypy(W_Root)
@@ -337,6 +340,7 @@
last_py_obj = w_pyobj
space.setattr(space.builtin_modules['gc'], space.newtext('garbage'),
w_list)
+ rawrefcount.end_garbage()
class PyObjDeallocAction(executioncontext.AsyncAction):
"""An action that invokes _Py_Dealloc() on the dying PyObjects.
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
@@ -163,7 +163,15 @@
# It does not need an additional copy in trace out
GCFLAG_SHADOW_INITIALIZED = first_gcflag << 11
-_GCFLAG_FIRST_UNUSED = first_gcflag << 12 # the first unused bit
+# Objects referenced only from legacy rawrefcount finalizers which have
+# not been added to gc.garbage have this flag set. It is set during the
+# rawrefcount marking phase and removed once the object is returned to
+# the caller, who is adding it to the gc.garbage list. During the next
+# collection cycle this process is repeated, as the set of objects might
+# have changed.
+GCFLAG_GARBAGE = first_gcflag << 12
+
+_GCFLAG_FIRST_UNUSED = first_gcflag << 13 # the first unused bit
# States for the incremental GC
@@ -2711,6 +2719,9 @@
# to also set TRACK_YOUNG_PTRS here, for the write barrier.
hdr.tid |= GCFLAG_VISITED | GCFLAG_TRACK_YOUNG_PTRS
+ if self.rrc_state == self.RAWREFCOUNT_STATE_MARKING:
+ hdr.tid |= GCFLAG_GARBAGE
+
if self.has_gcptr(llop.extract_ushort(llgroup.HALFWORD, hdr.tid)):
#
# Trace the content of the object and put all objects it references
@@ -3063,15 +3074,18 @@
rrc_enabled = False
- # The default state. Here cyclic garbage with legacy finalizers is marked.
+ # Default state, no rawrefcount specific code is executed during normal
marking.
RAWREFCOUNT_STATE_DEFAULT = 0
+ # Here cyclic garbage only reachable from legacy finalizers is marked.
+ RAWREFCOUNT_STATE_MARKING = 1
+
# The state in which cyclic garbage with legacy finalizers is traced.
# Do not mark objects during this state, because we remove the flag
# during tracing and we do not want to trace those objects again. Also
# during this phase no new objects can be marked, as we are only building
# the list of cyclic garbage.
- RAWREFCOUNT_STATE_GARBAGE = 1
+ RAWREFCOUNT_STATE_GARBAGE = 2
_ADDRARRAY = lltype.Array(llmemory.Address, hints={'nolength': True})
PYOBJ_HDR = lltype.Struct('GCHdr_PyObject',
@@ -3228,19 +3242,22 @@
gchdr.c_gc_next = next
next.c_gc_prev = gchdr
+ def rawrefcount_begin_garbage(self):
+ # after this, no new objects should be marked with the GCFLAG_GARBAGE
+ # flag
+ self.rrc_state = self.RAWREFCOUNT_STATE_GARBAGE
+
+ def rawrefcount_end_garbage(self):
+ # now there should not be any object left with a GCFLAG_GARBAGE flag
+ # set
+ self.rrc_state = self.RAWREFCOUNT_STATE_DEFAULT
+
def rawrefcount_next_garbage_pypy(self):
- if self.rrc_state == self.RAWREFCOUNT_STATE_DEFAULT:
- self.rrc_state = self.RAWREFCOUNT_STATE_GARBAGE
-
# return the next pypy object which is only reachable from garbage
# pyobjects, probably need two more colors for this. one for marking
# so that they stay alive during sweep, one for marking, so they do not
# get returned here again
- result = lltype.nullptr(llmemory.GCREF.TO)
-
- if result == lltype.nullptr(llmemory.GCREF.TO):
- self.rrc_state = self.RAWREFCOUNT_STATE_DEFAULT
- return result
+ return lltype.nullptr(llmemory.GCREF.TO)
def rawrefcount_next_garbage_pyobj(self, curr_pyobj):
# implement st objects in this list still remain in the set of
@@ -3249,7 +3266,6 @@
# to pyobjs in this list and might increment the refcount.
return llmemory.NULL
-
def rrc_invoke_callback(self):
if self.rrc_enabled and (self.rrc_dealloc_pending.non_empty() or
self.rrc_pyobj_isolate_list.c_gc_next <>
@@ -3365,61 +3381,63 @@
_rrc_free._always_inline_ = True
def rrc_major_collection_trace(self):
- if not self._rrc_gc_list_is_empty(self.rrc_pyobj_old_list):
- # Check, if the cyclic isolate from the last collection cycle
- # is reachable from outside, after the finalizers have been
- # executed.
- self._rrc_collect_rawrefcount_roots(self.rrc_pyobj_old_list)
- found_alive = False
+ if self.rrc_state == self.RAWREFCOUNT_STATE_DEFAULT:
+ if not self._rrc_gc_list_is_empty(self.rrc_pyobj_old_list):
+ # Check, if the cyclic isolate from the last collection cycle
+ # is reachable from outside, after the finalizers have been
+ # executed.
+ self._rrc_collect_rawrefcount_roots(self.rrc_pyobj_old_list)
+ found_alive = False
+ gchdr = self.rrc_pyobj_old_list.c_gc_next
+ while gchdr <> self.rrc_pyobj_old_list:
+ if (gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT) > 0:
+ found_alive = True
+ break
+ gchdr = gchdr.c_gc_next
+ if found_alive:
+ self._rrc_gc_list_merge(self.rrc_pyobj_old_list,
+ self.rrc_pyobj_list)
+ else:
+ self._rrc_gc_list_merge(self.rrc_pyobj_old_list,
+ self.rrc_pyobj_garbage_list)
+
+ self._rrc_collect_rawrefcount_roots(self.rrc_pyobj_list)
+ self._rrc_mark_rawrefcount()
+
+ # handle legacy finalizers
+ self.rrc_state = self.RAWREFCOUNT_STATE_MARKING
+ # TODO: move all pyobjs to sublist of pyobj_list and reset cyclic
refcount
+ # TODO: mark all pypy-objects with special flag and mark them
visited
+ self.rrc_state = self.RAWREFCOUNT_STATE_DEFAULT
+
+ # now handle modern finalizers
+ found_finalizer = False
gchdr = self.rrc_pyobj_old_list.c_gc_next
while gchdr <> self.rrc_pyobj_old_list:
- if (gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT) > 0:
- found_alive = True
- break
+ if self.rrc_finalizer_type(gchdr) == \
+ self.RAWREFCOUNT_FINALIZER_MODERN:
+ found_finalizer = True
gchdr = gchdr.c_gc_next
- if found_alive:
- self._rrc_gc_list_merge(self.rrc_pyobj_old_list,
- self.rrc_pyobj_list)
- else:
- self._rrc_gc_list_merge(self.rrc_pyobj_old_list,
- self.rrc_pyobj_garbage_list)
-
- self._rrc_collect_rawrefcount_roots(self.rrc_pyobj_list)
- self._rrc_mark_rawrefcount()
-
- found_finalizer = False
- gchdr = self.rrc_pyobj_old_list.c_gc_next
- while gchdr <> self.rrc_pyobj_old_list:
- if self.rrc_finalizer_type(gchdr) == \
- self.RAWREFCOUNT_FINALIZER_MODERN:
- found_finalizer = True
- gchdr = gchdr.c_gc_next
- if found_finalizer:
- self._rrc_gc_list_move(self.rrc_pyobj_old_list,
- self.rrc_pyobj_isolate_list)
-
- self.rrc_p_list_old.foreach(self._rrc_major_trace, found_finalizer)
- self.rrc_o_list_old.foreach(self._rrc_major_trace, found_finalizer)
-
- # TODO: for all unreachable objects, which are marked potentially
- # TODO: uncollectable, move them to the set of uncollectable objs
-
- # TODO: for all unreachable objects with tp_del (legacy finalizer),
- # TODO: except for border objects with a refcount of
- # TODO: REFCNT_FROM_PYPY (equals zero at this point):
- # TODO: * mark reachable pypy objects
- # TODO: * move reachable cpython objects back to pyobj_list
- # TODO: * mark all reachable objects as potentially uncollectable
+ if found_finalizer:
+ self._rrc_gc_list_move(self.rrc_pyobj_old_list,
+ self.rrc_pyobj_isolate_list)
+
+ use_cylicrefcnt = not found_finalizer
+ else:
+ use_cylicrefcnt = False
+
+ self.rrc_p_list_old.foreach(self._rrc_major_trace, use_cylicrefcnt)
+ self.rrc_o_list_old.foreach(self._rrc_major_trace, use_cylicrefcnt)
# TODO: handle weakrefs for unreachable objects and create
# TODO: a list of callbacks, which has to be called after the
# TODO: the GC runs
- def _rrc_major_trace(self, pyobject, found_finalizer):
+ def _rrc_major_trace(self, pyobject, use_cylicrefcnt):
from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
#
- if not found_finalizer:
+ if use_cylicrefcnt:
pygchdr = self.rrc_pyobj_as_gc(self._pyobj(pyobject))
if pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR):
rc = pygchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT
@@ -3457,9 +3475,10 @@
self.rrc_o_list_old.delete()
self.rrc_o_list_old = new_o_list
- if not self._rrc_gc_list_is_empty(self.rrc_pyobj_old_list):
- self._rrc_gc_list_merge(self.rrc_pyobj_old_list,
- self.rrc_pyobj_garbage_list)
+ if self.rrc_state == self.RAWREFCOUNT_STATE_DEFAULT:
+ if not self._rrc_gc_list_is_empty(self.rrc_pyobj_old_list):
+ self._rrc_gc_list_merge(self.rrc_pyobj_old_list,
+ self.rrc_pyobj_garbage_list)
def _rrc_major_free(self, pyobject, surviving_list, surviving_dict):
# The pyobject survives if the corresponding obj survives.
diff --git a/rpython/memory/gc/test/test_rawrefcount.py
b/rpython/memory/gc/test/test_rawrefcount.py
--- a/rpython/memory/gc/test/test_rawrefcount.py
+++ b/rpython/memory/gc/test/test_rawrefcount.py
@@ -627,6 +627,7 @@
self.gc.rawrefcount_cyclic_garbage_remove()
next_dead = self.gc.rawrefcount_cyclic_garbage_head()
+ self.gc.rawrefcount_begin_garbage()
next_garbage = self.gc.rawrefcount_next_garbage_pypy()
while next_garbage <> lltype.nullptr(llmemory.GCREF.TO):
garbage_pypy.append(next_garbage)
@@ -637,6 +638,7 @@
garbage_pyobj.append(next)
next = self.gc.rawrefcount_next_garbage_pyobj(last_pyobj)
last_pyobj = next
+ self.gc.rawrefcount_end_garbage()
# do a collection to find cyclic isolates and clean them, if there are
# no finalizers
diff --git a/rpython/memory/gctransform/framework.py
b/rpython/memory/gctransform/framework.py
--- a/rpython/memory/gctransform/framework.py
+++ b/rpython/memory/gctransform/framework.py
@@ -518,6 +518,12 @@
self.rawrefcount_cyclic_garbage_remove_ptr = getfn(
GCClass.rawrefcount_cyclic_garbage_remove, [s_gc],
annmodel.s_None, inline = True)
+ self.rawrefcount_begin_garbage_ptr = getfn(
+ GCClass.rawrefcount_begin_garbage, [s_gc], annmodel.s_None,
+ inline = True)
+ self.rawrefcount_end_garbage_ptr = getfn(
+ GCClass.rawrefcount_end_garbage, [s_gc], annmodel.s_None,
+ inline = True)
self.rawrefcount_next_garbage_pypy_ptr = getfn(
GCClass.rawrefcount_next_garbage_pypy, [s_gc],
s_gcref, inline = True)
@@ -1432,6 +1438,14 @@
[self.rawrefcount_cyclic_garbage_remove_ptr,
self.c_const_gc])
+ def gct_gc_rawrefcount_begin_garbage(self, hop):
+ hop.genop("direct_call",
+ [self.rawrefcount_begin_garbage_ptr, self.c_const_gc])
+
+ def gct_gc_rawrefcount_end_garbage(self, hop):
+ hop.genop("direct_call",
+ [self.rawrefcount_end_garbage_ptr, self.c_const_gc])
+
def gct_gc_rawrefcount_next_garbage_pypy(self, hop):
assert hop.spaceop.result.concretetype == llmemory.GCREF
hop.genop("direct_call",
diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py
--- a/rpython/rlib/rawrefcount.py
+++ b/rpython/rlib/rawrefcount.py
@@ -146,6 +146,14 @@
return lltype.nullptr(OB_PTR_TYPE.TO)
@not_rpython
+def begin_garbage():
+ pass
+
+@not_rpython
+def end_garbage():
+ pass
+
+@not_rpython
def next_garbage_pypy(Class):
return None
@@ -426,14 +434,20 @@
return _spec_p(hop, v_p)
class Entry(ExtRegistryEntry):
- _about_ = cyclic_garbage_remove
+ _about_ = (cyclic_garbage_remove, begin_garbage, end_garbage)
def compute_result_annotation(self):
pass
def specialize_call(self, hop):
hop.exception_cannot_occur()
- hop.genop('gc_rawrefcount_cyclic_garbage_remove', [])
+ if self.instance is cyclic_garbage_remove:
+ name = 'gc_rawrefcount_cyclic_garbage_remove'
+ elif self.instance is begin_garbage:
+ name = 'gc_rawrefcount_begin_garbage'
+ elif self.instance is end_garbage:
+ name = 'gc_rawrefcount_end_garbage'
+ hop.genop(name, [])
src_dir = py.path.local(__file__).dirpath() / 'src'
boehm_eci = ExternalCompilationInfo(
diff --git a/rpython/rtyper/llinterp.py b/rpython/rtyper/llinterp.py
--- a/rpython/rtyper/llinterp.py
+++ b/rpython/rtyper/llinterp.py
@@ -995,6 +995,10 @@
def op_gc_rawrefcount_cyclic_garbage_remove(self, *args):
raise NotImplementedError("gc_rawrefcount_cyclic_garbage_remove")
+ def op_gc_rawrefcount_begin_garbage(self, *args):
+ raise NotImplementedError("gc_rawrefcount_begin_garbage")
+ def op_gc_rawrefcount_end_garbage(self, *args):
+ raise NotImplementedError("gc_rawrefcount_end_garbage")
def op_gc_rawrefcount_next_garbage_pypy(self, *args):
raise NotImplementedError("gc_rawrefcount_next_garbage_pypy")
def op_gc_rawrefcount_next_garbage_pyobj(self, *args):
diff --git a/rpython/rtyper/lltypesystem/lloperation.py
b/rpython/rtyper/lltypesystem/lloperation.py
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -532,6 +532,8 @@
'gc_rawrefcount_next_cyclic_isolate': LLOp(),
'gc_rawrefcount_cyclic_garbage_head': LLOp(sideeffects=False),
'gc_rawrefcount_cyclic_garbage_remove': LLOp(),
+ 'gc_rawrefcount_begin_garbage': LLOp(),
+ 'gc_rawrefcount_end_garbage': LLOp(),
'gc_rawrefcount_next_garbage_pypy': LLOp(),
'gc_rawrefcount_next_garbage_pyobj': LLOp(),
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit