[pypy-commit] pypy cpyext-gc-cycle: Clear weakref callbacks in rawrefcounted garbage to avoid resurrection
stevie_92
pypy.commits at gmail.com
Fri Apr 12 04:48:42 EDT 2019
Author: Stefan Beyer <home at sbeyer.at>
Branch: cpyext-gc-cycle
Changeset: r96453:0b7ae798b964
Date: 2019-04-12 10:47 +0200
http://bitbucket.org/pypy/pypy/changeset/0b7ae798b964/
Log: Clear weakref callbacks in rawrefcounted garbage to avoid
resurrection
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
@@ -160,6 +160,20 @@
pyobj_dealloc_action = PyObjDeallocAction(space)
self.dealloc_trigger = lambda: pyobj_dealloc_action.fire()
+ def _clear_weakref_callbacks(gcref):
+ from pypy.module._weakref.interp__weakref import \
+ W_Weakref, W_CallableProxy
+ from pypy.module.gc.referents import \
+ try_cast_gcref_to_w_root
+ w_obj = try_cast_gcref_to_w_root(gcref)
+ if type(w_obj) is W_Weakref:
+ w_obj.w_callable = None
+ elif type(w_obj) is W_CallableProxy:
+ w_obj.w_callable = None
+
+ self.clear_weakref_callbacks = \
+ (lambda w_obj: _clear_weakref_callbacks(w_obj))
+
def _tp_traverse(pyobj_ptr, callback, args):
from pypy.module.cpyext.api import PyObject
from pypy.module.cpyext.typeobjectdefs import visitproc
@@ -213,7 +227,9 @@
self.tp_traverse),
pypyobj_list,
self.C._PyPy_gc_as_pyobj, self.C._PyPy_pyobj_as_gc,
- self.C._PyPy_finalizer_type)
+ self.C._PyPy_finalizer_type,
+ llhelper(rawrefcount.RAWREFCOUNT_CLEAR_WR_TYPE,
+ self.clear_weakref_callbacks))
self.builder.attach_all(space)
setup_new_method_def(space)
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
@@ -3112,6 +3112,8 @@
PYOBJ_GC_HDR_PTR))
RAWREFCOUNT_FINALIZER_TYPE = lltype.Ptr(lltype.FuncType([PYOBJ_GC_HDR_PTR],
lltype.Signed))
+ RAWREFCOUNT_CLEAR_WR_TYPE = lltype.Ptr(lltype.FuncType([llmemory.GCREF],
+ lltype.Void))
RAWREFCOUNT_FINALIZER_NONE = 0
RAWREFCOUNT_FINALIZER_MODERN = 1
RAWREFCOUNT_FINALIZER_LEGACY = 2
@@ -3124,7 +3126,8 @@
return llmemory.cast_adr_to_ptr(pygchdraddr, self.PYOBJ_GC_HDR_PTR)
def rawrefcount_init(self, dealloc_trigger_callback, tp_traverse,
- pyobj_list, gc_as_pyobj, pyobj_as_gc, finalizer_type):
+ pyobj_list, gc_as_pyobj, pyobj_as_gc, finalizer_type,
+ clear_weakref_callback):
# see pypy/doc/discussion/rawrefcount.rst
if not self.rrc_enabled:
self.rrc_p_list_young = self.AddressStack()
@@ -3145,6 +3148,7 @@
self.rrc_gc_as_pyobj = gc_as_pyobj
self.rrc_pyobj_as_gc = pyobj_as_gc
self.rrc_finalizer_type = finalizer_type
+ self.rrc_clear_weakref_callback = clear_weakref_callback
self.rrc_enabled = True
self.rrc_state = self.RAWREFCOUNT_STATE_DEFAULT
@@ -3411,10 +3415,6 @@
self.rrc_p_list_old.foreach(self._rrc_major_trace, use_cylicrc)
self.rrc_o_list_old.foreach(self._rrc_major_trace, use_cylicrc)
- # 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, use_cylicrefcnt):
from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
@@ -3459,9 +3459,48 @@
if self.rrc_state == self.RAWREFCOUNT_STATE_DEFAULT:
if not self._rrc_gc_list_is_empty(self.rrc_pyobj_old_list):
+ self._rrc_clear_weakref_callbacks()
self._rrc_gc_list_merge(self.rrc_pyobj_old_list,
self.rrc_pyobj_dead_list)
+ def _rrc_clear_weakref_callbacks(self):
+ # Look for any weakrefs within the trash cycle and remove the callback.
+ # This is only needed for weakrefs created from rawrefcounted objects
+ # because weakrefs from gc-managed objects are going away anyway.
+ gchdr = self.rrc_pyobj_old_list.c_gc_next
+ while gchdr <> self.rrc_pyobj_old_list:
+ pyobj = self.rrc_gc_as_pyobj(gchdr)
+ self._rrc_traverse_weakref(pyobj)
+ gchdr = gchdr.c_gc_next
+
+ def _rrc_visit_weakref(pyobj, self_ptr):
+ from rpython.rtyper.annlowlevel import cast_adr_to_nongc_instance
+ #
+ self_adr = rffi.cast(llmemory.Address, self_ptr)
+ self = cast_adr_to_nongc_instance(IncrementalMiniMarkGC, self_adr)
+ self._rrc_visit_weakref_action(pyobj, None)
+ return rffi.cast(rffi.INT_real, 0)
+
+ def _rrc_visit_weakref_action(self, pyobj, ignore):
+ intobj = pyobj.c_ob_pypy_link
+ if intobj <> 0:
+ obj = llmemory.cast_int_to_adr(intobj)
+ object = llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
+ self.rrc_clear_weakref_callback(object)
+
+ def _rrc_traverse_weakref(self, pyobj):
+ from rpython.rlib.objectmodel import we_are_translated
+ from rpython.rtyper.annlowlevel import (cast_nongc_instance_to_adr,
+ llhelper)
+ #
+ if we_are_translated():
+ callback_ptr = llhelper(self.RAWREFCOUNT_VISIT,
+ IncrementalMiniMarkGC._rrc_visit_weakref)
+ self_ptr = rffi.cast(rffi.VOIDP, cast_nongc_instance_to_adr(self))
+ self.rrc_tp_traverse(pyobj, callback_ptr, self_ptr)
+ else:
+ self.rrc_tp_traverse(pyobj, self._rrc_visit_weakref_action, None)
+
def _rrc_major_free(self, pyobject, surviving_list, surviving_dict):
# The pyobject survives if the corresponding obj survives.
# This is true if the obj has one of the following two flags:
@@ -3658,6 +3697,7 @@
self._rrc_gc_list_merge(self.rrc_pyobj_old_list,
self.rrc_pyobj_list)
else:
+ self._rrc_clear_weakref_callbacks()
self._rrc_gc_list_merge(self.rrc_pyobj_old_list,
self.rrc_pyobj_dead_list)
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
@@ -486,7 +486,8 @@
SomePtr(GCClass.RAWREFCOUNT_TRAVERSE), SomeAddress(),
SomePtr(GCClass.RAWREFCOUNT_GC_AS_PYOBJ),
SomePtr(GCClass.RAWREFCOUNT_PYOBJ_AS_GC),
- SomePtr(GCClass.RAWREFCOUNT_FINALIZER_TYPE)],
+ SomePtr(GCClass.RAWREFCOUNT_FINALIZER_TYPE),
+ SomePtr(GCClass.RAWREFCOUNT_CLEAR_WR_TYPE)],
annmodel.s_None)
self.rawrefcount_create_link_pypy_ptr = getfn(
GCClass.rawrefcount_create_link_pypy,
@@ -1365,7 +1366,7 @@
def gct_gc_rawrefcount_init(self, hop):
[v_fnptr, v_fnptr2, v_pyobj_list, v_fnptr3, v_fnptr4,
- v_fnptr5] = hop.spaceop.args
+ v_fnptr5, v_fnptr6] = hop.spaceop.args
assert v_fnptr.concretetype == self.GCClass.RAWREFCOUNT_DEALLOC_TRIGGER
assert v_fnptr2.concretetype == self.GCClass.RAWREFCOUNT_TRAVERSE
# TODO add assert for v_pyobj_list, improve asserts (types not same but equal)
@@ -1373,7 +1374,8 @@
# assert v_fnptr4.concretetype == self.GCClass.RAWREFCOUNT_PYOBJ_AS_GC
hop.genop("direct_call",
[self.rawrefcount_init_ptr, self.c_const_gc, v_fnptr,
- v_fnptr2, v_pyobj_list, v_fnptr3, v_fnptr4, v_fnptr5])
+ v_fnptr2, v_pyobj_list, v_fnptr3, v_fnptr4, v_fnptr5,
+ v_fnptr6])
def gct_gc_rawrefcount_create_link_pypy(self, hop):
[v_gcobj, v_pyobject] = hop.spaceop.args
diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py
--- a/rpython/rlib/rawrefcount.py
+++ b/rpython/rlib/rawrefcount.py
@@ -36,6 +36,8 @@
PYOBJ_HDR_PTR))
RAWREFCOUNT_PYOBJ_AS_GC = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR],
PYOBJ_GC_HDR_PTR))
+RAWREFCOUNT_CLEAR_WR_TYPE = lltype.Ptr(lltype.FuncType([llmemory.GCREF],
+ lltype.Void))
def _build_pypy_link(p):
@@ -307,21 +309,22 @@
def compute_result_annotation(self, s_dealloc_callback, s_tp_traverse,
s_pyobj_list, s_as_gc, s_as_pyobj,
- a_finalizer_type):
+ a_finalizer_type, a_clear_wr):
from rpython.rtyper.llannotation import SomePtr
assert isinstance(s_dealloc_callback, SomePtr) # ll-ptr-to-function
assert isinstance(s_tp_traverse, SomePtr)
assert isinstance(s_as_gc, SomePtr)
assert isinstance(s_as_pyobj, SomePtr)
assert isinstance(a_finalizer_type, SomePtr)
+ assert isinstance(a_clear_wr, SomePtr)
def specialize_call(self, hop):
hop.exception_cannot_occur()
v_dealloc_callback, v_tp_traverse, v_pyobj_list, v_as_gc, \
- v_as_pyobj, v_finalizer_type = hop.inputargs(*hop.args_r)
+ v_as_pyobj, v_finalizer_type, v_clear_wr = hop.inputargs(*hop.args_r)
hop.genop('gc_rawrefcount_init', [v_dealloc_callback, v_tp_traverse,
v_pyobj_list, v_as_gc, v_as_pyobj,
- v_finalizer_type])
+ v_finalizer_type, v_clear_wr])
class Entry(ExtRegistryEntry):
More information about the pypy-commit
mailing list