[pypy-commit] pypy cpyext-gc-cycle: Implemented tests for gc.garbage list and adapted interface
stevie_92
pypy.commits at gmail.com
Fri Mar 1 08:14:13 EST 2019
Author: Stefan Beyer <home at sbeyer.at>
Branch: cpyext-gc-cycle
Changeset: r96197:15bed0a0192d
Date: 2019-03-01 14:13 +0100
http://bitbucket.org/pypy/pypy/changeset/15bed0a0192d/
Log: Implemented tests for gc.garbage list and adapted 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
@@ -77,6 +77,7 @@
space = self.space
if not self.space.config.translating:
def dealloc_trigger():
+ from pypy.interpreter.baseobjspace import W_Root
from pypy.module.cpyext.pyobject import PyObject, decref, \
incref, cts, finalize, from_ref
w_list = space.getattr(space.builtin_modules['gc'],
@@ -116,10 +117,15 @@
llmemory.cast_ptr_to_adr(head)):
rawrefcount.cyclic_garbage_remove()
while True:
- py_obj = rawrefcount.next_garbage(PyObject)
+ w_obj = rawrefcount.next_garbage_pypy(W_Root)
if not py_obj:
break
- w_obj = from_ref(space, py_obj)
+ w_list.append(w_obj)
+ while True:
+ w_pyobj = rawrefcount.next_garbage_pyobj(PyObject)
+ if not py_obj:
+ break
+ w_obj = from_ref(space, w_pyobj)
w_list.append(w_obj)
print 'dealloc_trigger DONE'
return "RETRY"
@@ -282,6 +288,7 @@
def _rawrefcount_perform(space):
+ from pypy.interpreter.baseobjspace import W_Root
from pypy.module.cpyext.pyobject import (PyObject, incref, decref,
finalize, from_ref)
@@ -315,10 +322,15 @@
rawrefcount.cyclic_garbage_remove()
while True:
- py_obj = rawrefcount.next_garbage(PyObject)
+ w_obj = rawrefcount.next_garbage_pypy(W_Root)
if not py_obj:
break
- w_obj = from_ref(space, py_obj)
+ w_list.append(w_obj)
+ while True:
+ w_pyobj = rawrefcount.next_garbage_pyobj(PyObject)
+ if not py_obj:
+ break
+ w_obj = from_ref(space, w_pyobj)
w_list.append(w_obj)
class PyObjDeallocAction(executioncontext.AsyncAction):
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
@@ -3217,7 +3217,22 @@
gchdr.c_gc_next = next
next.c_gc_prev = gchdr
- def rawrefcount_next_garbage(self):
+ def rawrefcount_next_garbage_pypy(self):
+ # 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
+ return lltype.nullptr(llmemory.GCREF.TO)
+
+ def rawrefcount_next_garbage_pyobj(self):
+ # implement st objects in this list still remain in the set of
+ # all pyobjs, because references could still change and cause them
+ # to live again. also keep in mind, that state will create references
+ # to pyobjs in this list and might increment the refcount.
+
+ # use create_link_pyobj on the result to create gc objects for pyobjects
+ #p = W_Root(42)
+ #p.pyobj = ob
return llmemory.NULL
diff --git a/rpython/memory/gc/test/dot/garbage_cpython_simple_1.dot b/rpython/memory/gc/test/dot/garbage_cpython_simple_1.dot
new file mode 100644
--- /dev/null
+++ b/rpython/memory/gc/test/dot/garbage_cpython_simple_1.dot
@@ -0,0 +1,6 @@
+digraph G {
+ "a" [type=C, alive=y, garbage=y, finalizer=legacy];
+ "b" [type=C, alive=y, garbage=y];
+ "a" -> "b";
+ "b" -> "a";
+}
\ No newline at end of file
diff --git a/rpython/memory/gc/test/dot/garbage_cross_simple_1.dot b/rpython/memory/gc/test/dot/garbage_cross_simple_1.dot
new file mode 100644
--- /dev/null
+++ b/rpython/memory/gc/test/dot/garbage_cross_simple_1.dot
@@ -0,0 +1,10 @@
+digraph G {
+ "a" [type=C, alive=y, garbage=y, finalizer=legacy];
+ "b" [type=B, alive=y, garbage=y];
+ "c" [type=P, alive=y, garbage=y];
+ "d" [type=P, alive=y, rooted=y];
+ "a" -> "b";
+ "b" -> "a";
+ "b" -> "c";
+ "c" -> "d";
+}
\ No newline at end of file
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
@@ -1,17 +1,17 @@
import os, py
from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
-from rpython.memory.gc.incminimark import IncrementalMiniMarkGC
+from rpython.memory.gc.incminimark import IncrementalMiniMarkGC as IncMiniMark
from rpython.memory.gc.test.test_direct import BaseDirectGCTest
-from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
-from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
-PYOBJ_HDR = IncrementalMiniMarkGC.PYOBJ_HDR
-PYOBJ_HDR_PTR = IncrementalMiniMarkGC.PYOBJ_HDR_PTR
-RAWREFCOUNT_VISIT = IncrementalMiniMarkGC.RAWREFCOUNT_VISIT
-PYOBJ_GC_HDR = IncrementalMiniMarkGC.PYOBJ_GC_HDR
-PYOBJ_GC_HDR_PTR = IncrementalMiniMarkGC.PYOBJ_GC_HDR_PTR
-RAWREFCOUNT_FINALIZER_MODERN = \
- IncrementalMiniMarkGC.RAWREFCOUNT_FINALIZER_MODERN
-RAWREFCOUNT_FINALIZER_NONE = IncrementalMiniMarkGC.RAWREFCOUNT_FINALIZER_NONE
+from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY, REFCNT_FROM_PYPY_LIGHT
+
+PYOBJ_HDR = IncMiniMark.PYOBJ_HDR
+PYOBJ_HDR_PTR = IncMiniMark.PYOBJ_HDR_PTR
+RAWREFCOUNT_VISIT = IncMiniMark.RAWREFCOUNT_VISIT
+PYOBJ_GC_HDR = IncMiniMark.PYOBJ_GC_HDR
+PYOBJ_GC_HDR_PTR = IncMiniMark.PYOBJ_GC_HDR_PTR
+RAWREFCOUNT_FINALIZER_MODERN = IncMiniMark.RAWREFCOUNT_FINALIZER_MODERN
+RAWREFCOUNT_FINALIZER_LEGACY = IncMiniMark.RAWREFCOUNT_FINALIZER_LEGACY
+RAWREFCOUNT_FINALIZER_NONE = IncMiniMark.RAWREFCOUNT_FINALIZER_NONE
S = lltype.GcForwardReference()
S.become(lltype.GcStruct('S',
@@ -21,7 +21,7 @@
class TestRawRefCount(BaseDirectGCTest):
- GCClass = IncrementalMiniMarkGC
+ GCClass = IncMiniMark
def setup_method(self, method):
BaseDirectGCTest.setup_method(self, method)
@@ -448,13 +448,14 @@
class NodeInfo:
def __init__(self, type, alive, ext_refcnt, finalizer, resurrect,
- delete):
+ delete, garbage):
self.type = type
self.alive = alive
self.ext_refcnt = ext_refcnt
self.finalizer = finalizer
self.resurrect = resurrect
self.delete = delete
+ self.garbage = garbage
path = os.path.join(self.dot_dir, file)
g = pydot.graph_from_dot_file(path)[0]
@@ -475,8 +476,9 @@
finalizers = True
resurrect = attr['resurrect'] if 'resurrect' in attr else None
delete = attr['delete'] if 'delete' in attr else None
+ garbage = True if 'garbage' in attr else False
info = NodeInfo(type, alive, ext_refcnt, finalizer, resurrect,
- delete)
+ delete, garbage)
if type == "C":
r, raddr, check_alive = self._rawrefcount_pyobj()
r.c_ob_refcnt += ext_refcnt
@@ -526,6 +528,8 @@
nodes[resurrect].info.ext_refcnt += 1
if delete is not None:
self._rawrefcount_add_delete(n.r, nodes[delete].r)
+ elif n.info.finalizer == "legacy":
+ self.pyobj_finalizer[index] = RAWREFCOUNT_FINALIZER_LEGACY
else:
self.pyobj_finalizer[index] = RAWREFCOUNT_FINALIZER_NONE
@@ -545,6 +549,8 @@
self.gc.rrc_tp_traverse(source.r, append, None)
assert len(dests_target) == 0
+ garbage_pypy = []
+ garbage_pyobj = []
def cleanup():
# do cleanup after collection (clear all dead pyobjects)
def finalize_modern(pyobj):
@@ -621,6 +627,13 @@
self.gc.rawrefcount_cyclic_garbage_remove()
next_dead = self.gc.rawrefcount_cyclic_garbage_head()
+ next = self.gc.rawrefcount_next_garbage_pypy()
+ while next <> lltype.nullptr(llmemory.GCREF.TO):
+ garbage_pypy.append(next)
+ next = self.gc.rawrefcount_next_garbage_pyobj()
+ while next <> llmemory.NULL:
+ garbage_pyobj.append(next)
+
# do a collection to find cyclic isolates and clean them, if there are
# no finalizers
self.gc.collect()
@@ -649,3 +662,16 @@
py.test.raises(RuntimeError, "n.p.x") # dead
else:
py.test.raises(RuntimeError, "n.r.c_ob_refcnt") # dead
+
+ # check if unreachable objects in cyclic structures with legacy
+ # finalizers and all otherwise unreachable objects reachable from them
+ # have been added to the garbage list
+ for name in nodes:
+ n = nodes[name]
+ if n.info.alive:
+ if n.info.type == "C":
+ assert n.info.garbage != (n.raddr not in garbage_pyobj)
+ else:
+ assert n.info.garbage != (n.pref not in garbage_pypy)
+ else:
+ assert not n.info.garbage
\ No newline at end of file
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,8 +518,11 @@
self.rawrefcount_cyclic_garbage_remove_ptr = getfn(
GCClass.rawrefcount_cyclic_garbage_remove, [s_gc],
annmodel.s_None, inline = True)
- self.rawrefcount_next_garbage_ptr = getfn(
- GCClass.rawrefcount_next_garbage, [s_gc],
+ self.rawrefcount_next_garbage_pypy_ptr = getfn(
+ GCClass.rawrefcount_next_garbage_pypy, [s_gc],
+ s_gcref, inline = True)
+ self.rawrefcount_next_garbage_pyobj_ptr = getfn(
+ GCClass.rawrefcount_next_garbage_pyobj, [s_gc],
SomeAddress(), inline = True)
if GCClass.can_usually_pin_objects:
@@ -1429,10 +1432,16 @@
[self.rawrefcount_cyclic_garbage_remove_ptr,
self.c_const_gc])
- def gct_gc_rawrefcount_next_garbage(self, hop):
+ def gct_gc_rawrefcount_next_garbage_pypy(self, hop):
+ assert hop.spaceop.result.concretetype == llmemory.GCREF
+ hop.genop("direct_call",
+ [self.rawrefcount_next_garbage_pypy_ptr, self.c_const_gc],
+ resultvar=hop.spaceop.result)
+
+ def gct_gc_rawrefcount_next_garbage_pyobj(self, hop):
assert hop.spaceop.result.concretetype == llmemory.Address
hop.genop("direct_call",
- [self.rawrefcount_next_garbage_ptr, self.c_const_gc],
+ [self.rawrefcount_next_garbage_pyobj_ptr, self.c_const_gc],
resultvar=hop.spaceop.result)
def _set_into_gc_array_part(self, op):
diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py
--- a/rpython/rlib/rawrefcount.py
+++ b/rpython/rlib/rawrefcount.py
@@ -146,7 +146,11 @@
return lltype.nullptr(OB_PTR_TYPE.TO)
@not_rpython
-def next_garbage(OB_PTR_TYPE):
+def next_garbage_pypy(Class):
+ return lltype.nullptr(llmemory.GCREF.TO)
+
+ at not_rpython
+def next_garbage_pyobj(OB_PTR_TYPE):
return lltype.nullptr(OB_PTR_TYPE.TO)
@not_rpython
@@ -372,7 +376,7 @@
class Entry(ExtRegistryEntry):
_about_ = (next_dead, cyclic_garbage_head, next_cyclic_isolate,
- next_garbage)
+ next_garbage_pyobj)
def compute_result_annotation(self, s_OB_PTR_TYPE):
from rpython.rtyper.llannotation import lltype_to_annotation
@@ -386,13 +390,28 @@
name = 'gc_rawrefcount_cyclic_garbage_head'
elif self.instance is next_cyclic_isolate:
name = 'gc_rawrefcount_next_cyclic_isolate'
- elif self.instance is next_garbage:
- name = 'gc_rawrefcount_next_garbage'
+ elif self.instance is next_garbage_pyobj:
+ name = 'gc_rawrefcount_next_garbage_pyobj'
hop.exception_cannot_occur()
v_ob = hop.genop(name, [], resulttype = llmemory.Address)
return _spec_ob(hop, v_ob)
class Entry(ExtRegistryEntry):
+ _about_ = next_garbage_pypy
+
+ def compute_result_annotation(self, s_Class):
+ from rpython.annotator import model as annmodel
+ assert s_Class.is_constant()
+ classdef = self.bookkeeper.getuniqueclassdef(s_Class.const)
+ return annmodel.SomeInstance(classdef, can_be_None=True)
+
+ def specialize_call(self, hop):
+ hop.exception_cannot_occur()
+ v_p = hop.genop('gc_rawrefcount_next_garbage_pypy', [],
+ resulttype = llmemory.GCREF)
+ return _spec_p(hop, v_p)
+
+class Entry(ExtRegistryEntry):
_about_ = cyclic_garbage_remove
def compute_result_annotation(self):
diff --git a/rpython/rtyper/llinterp.py b/rpython/rtyper/llinterp.py
--- a/rpython/rtyper/llinterp.py
+++ b/rpython/rtyper/llinterp.py
@@ -995,8 +995,10 @@
def op_gc_rawrefcount_cyclic_garbage_remove(self, *args):
raise NotImplementedError("gc_rawrefcount_cyclic_garbage_remove")
- def op_gc_rawrefcount_next_garbage(self, *args):
- raise NotImplementedError("gc_rawrefcount_next_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):
+ raise NotImplementedError("gc_rawrefcount_next_garbage_pyobj")
def op_do_malloc_fixedsize(self):
raise NotImplementedError("do_malloc_fixedsize")
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,7 +532,8 @@
'gc_rawrefcount_next_cyclic_isolate': LLOp(),
'gc_rawrefcount_cyclic_garbage_head': LLOp(sideeffects=False),
'gc_rawrefcount_cyclic_garbage_remove': LLOp(),
- 'gc_rawrefcount_next_garbage': LLOp(),
+ 'gc_rawrefcount_next_garbage_pypy': LLOp(),
+ 'gc_rawrefcount_next_garbage_pyobj': LLOp(),
'gc_move_out_of_nursery': LLOp(),
More information about the pypy-commit
mailing list