[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