[pypy-commit] pypy use-gc-del-3: Port _weakref: enqueue_for_destruction() doesn't exist any more

arigo pypy.commits at gmail.com
Thu May 5 15:55:46 EDT 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: use-gc-del-3
Changeset: r84229:9fbfc373d95b
Date: 2016-05-05 21:55 +0200
http://bitbucket.org/pypy/pypy/changeset/9fbfc373d95b/

Log:	Port _weakref: enqueue_for_destruction() doesn't exist any more

diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -533,18 +533,6 @@
             return
         self._run_finalizers()
 
-    def _report_error(self, e, where, w_obj):
-        space = self.space
-        if isinstance(e, OperationError):
-            e.write_unraisable(space, where, w_obj)
-            e.clear(space)   # break up reference cycles
-        else:
-            addrstring = w_obj.getaddrstring(space)
-            msg = ("RPython exception %s in %s<%s at 0x%s> ignored\n" % (
-                       str(e), where, space.type(w_obj).name, addrstring))
-            space.call_method(space.sys.get('stderr'), 'write',
-                              space.wrap(msg))
-
     def _run_finalizers(self):
         while True:
             w_obj = self.space.finalizer_queue.next_dead()
@@ -558,13 +546,25 @@
             try:
                 self.space.userdel(w_obj)
             except Exception as e:
-                self._report_error(e, "method __del__ of ", w_obj)
+                report_error(self.space, e, "method __del__ of ", w_obj)
 
             # Call the RPython-level _finalize_() method.
             try:
                 w_obj._finalize_()
             except Exception as e:
-                self._report_error(e, "finalizer of ", w_obj)
+                report_error(self.space, e, "finalizer of ", w_obj)
+
+
+def report_error(space, e, where, w_obj):
+    if isinstance(e, OperationError):
+        e.write_unraisable(space, where, w_obj)
+        e.clear(space)   # break up reference cycles
+    else:
+        addrstring = w_obj.getaddrstring(space)
+        msg = ("RPython exception %s in %s<%s at 0x%s> ignored\n" % (
+                   str(e), where, space.type(w_obj).name, addrstring))
+        space.call_method(space.sys.get('stderr'), 'write',
+                          space.wrap(msg))
 
 
 def make_finalizer_queue(W_Root, space):
diff --git a/pypy/module/_weakref/interp__weakref.py b/pypy/module/_weakref/interp__weakref.py
--- a/pypy/module/_weakref/interp__weakref.py
+++ b/pypy/module/_weakref/interp__weakref.py
@@ -3,7 +3,8 @@
 from pypy.interpreter.error import oefmt
 from pypy.interpreter.gateway import interp2app, ObjSpace
 from pypy.interpreter.typedef import TypeDef
-from rpython.rlib import jit
+from pypy.interpreter.executioncontext import AsyncAction, report_error
+from rpython.rlib import jit, rgc
 from rpython.rlib.rshrinklist import AbstractShrinkList
 from rpython.rlib.objectmodel import specialize
 from rpython.rlib.rweakref import dead_ref
@@ -19,6 +20,7 @@
     cached_weakref  = None
     cached_proxy    = None
     other_refs_weak = None
+    has_callbacks   = False
 
     def __init__(self, space):
         self.space = space
@@ -99,31 +101,11 @@
                     return w_ref
         return space.w_None
 
-
-class WeakrefLifelineWithCallbacks(WeakrefLifeline):
-
-    def __init__(self, space, oldlifeline=None):
-        self.space = space
-        if oldlifeline is not None:
-            self.cached_weakref = oldlifeline.cached_weakref
-            self.cached_proxy = oldlifeline.cached_proxy
-            self.other_refs_weak = oldlifeline.other_refs_weak
-
-    def __del__(self):
-        """This runs when the interp-level object goes away, and allows
-        its lifeline to go away.  The purpose of this is to activate the
-        callbacks even if there is no __del__ method on the interp-level
-        W_Root subclass implementing the object.
-        """
-        if self.other_refs_weak is None:
-            return
-        items = self.other_refs_weak.items()
-        for i in range(len(items)-1, -1, -1):
-            w_ref = items[i]()
-            if w_ref is not None and w_ref.w_callable is not None:
-                w_ref.enqueue_for_destruction(self.space,
-                                              W_WeakrefBase.activate_callback,
-                                              'weakref callback of ')
+    def enable_callbacks(self):
+        if not self.has_callbacks:
+            fq = self.space.fromcache(Cache).fq
+            fq.register_finalizer(self)
+            self.has_callbacks = True
 
     @jit.dont_look_inside
     def make_weakref_with_callback(self, w_subtype, w_obj, w_callable):
@@ -131,6 +113,7 @@
         w_ref = space.allocate_instance(W_Weakref, w_subtype)
         W_Weakref.__init__(w_ref, space, w_obj, w_callable)
         self.append_wref_to(w_ref)
+        self.enable_callbacks()
         return w_ref
 
     @jit.dont_look_inside
@@ -141,8 +124,44 @@
         else:
             w_proxy = W_Proxy(space, w_obj, w_callable)
         self.append_wref_to(w_proxy)
+        self.enable_callbacks()
         return w_proxy
 
+
+class WeakrefCallbackAction(AsyncAction):
+    """An action that runs when a W_Root object goes away, and allows
+    its lifeline to go away.  It activates all the callbacks of all
+    the dying lifelines.
+    """
+
+    def perform(self, executioncontext, frame):
+        fq = self.space.fromcache(Cache).fq
+        while True:
+            lifeline = fq.next_dead()
+            if lifeline is None:
+                break
+            if lifeline.other_refs_weak is None:
+                continue  # should never be the case, but better safe than sorry
+            items = lifeline.other_refs_weak.items()
+            for i in range(len(items)-1, -1, -1):
+                w_ref = items[i]()
+                if w_ref is not None and w_ref.w_callable is not None:
+                    try:
+                        w_ref.activate_callback()
+                    except Exception as e:
+                        report_error(self.space, e,
+                                     "weakref callback ", w_ref.w_callable)
+
+class Cache:
+    def __init__(self, space):
+        class WeakrefFinalizerQueue(rgc.FinalizerQueue):
+            Class = WeakrefLifeline
+            def finalizer_trigger(self):
+                space.weakref_callback_action.fire()
+        space.weakref_callback_action = WeakrefCallbackAction(space)
+        self.fq = WeakrefFinalizerQueue()
+
+
 # ____________________________________________________________
 
 
@@ -163,7 +182,6 @@
         self.w_obj_weak = dead_ref
 
     def activate_callback(w_self):
-        assert isinstance(w_self, W_WeakrefBase)
         w_self.space.call_function(w_self.w_callable, w_self)
 
     def descr__repr__(self, space):
@@ -227,32 +245,16 @@
         w_obj.setweakref(space, lifeline)
     return lifeline
 
-def getlifelinewithcallbacks(space, w_obj):
-    lifeline = w_obj.getweakref()
-    if not isinstance(lifeline, WeakrefLifelineWithCallbacks):  # or None
-        oldlifeline = lifeline
-        lifeline = WeakrefLifelineWithCallbacks(space, oldlifeline)
-        w_obj.setweakref(space, lifeline)
-    return lifeline
-
-
-def get_or_make_weakref(space, w_subtype, w_obj):
-    return getlifeline(space, w_obj).get_or_make_weakref(w_subtype, w_obj)
-
-
-def make_weakref_with_callback(space, w_subtype, w_obj, w_callable):
-    lifeline = getlifelinewithcallbacks(space, w_obj)
-    return lifeline.make_weakref_with_callback(w_subtype, w_obj, w_callable)
-
 
 def descr__new__weakref(space, w_subtype, w_obj, w_callable=None,
                         __args__=None):
     if __args__.arguments_w:
         raise oefmt(space.w_TypeError, "__new__ expected at most 2 arguments")
+    lifeline = getlifeline(space, w_obj)
     if space.is_none(w_callable):
-        return get_or_make_weakref(space, w_subtype, w_obj)
+        return lifeline.get_or_make_weakref(w_subtype, w_obj)
     else:
-        return make_weakref_with_callback(space, w_subtype, w_obj, w_callable)
+        return lifeline.make_weakref_with_callback(w_subtype, w_obj, w_callable)
 
 W_Weakref.typedef = TypeDef("weakref",
     __doc__ = """A weak reference to an object 'obj'.  A 'callback' can be given,
@@ -308,23 +310,15 @@
         return space.call_args(w_obj, __args__)
 
 
-def get_or_make_proxy(space, w_obj):
-    return getlifeline(space, w_obj).get_or_make_proxy(w_obj)
-
-
-def make_proxy_with_callback(space, w_obj, w_callable):
-    lifeline = getlifelinewithcallbacks(space, w_obj)
-    return lifeline.make_proxy_with_callback(w_obj, w_callable)
-
-
 def proxy(space, w_obj, w_callable=None):
     """Create a proxy object that weakly references 'obj'.
 'callback', if given, is called with the proxy as an argument when 'obj'
 is about to be finalized."""
+    lifeline = getlifeline(space, w_obj)
     if space.is_none(w_callable):
-        return get_or_make_proxy(space, w_obj)
+        return lifeline.get_or_make_proxy(w_obj)
     else:
-        return make_proxy_with_callback(space, w_obj, w_callable)
+        return lifeline.make_proxy_with_callback(w_obj, w_callable)
 
 def descr__new__proxy(space, w_subtype, w_obj, w_callable=None):
     raise oefmt(space.w_TypeError, "cannot create 'weakproxy' instances")
diff --git a/pypy/module/_weakref/test/test_weakref.py b/pypy/module/_weakref/test/test_weakref.py
--- a/pypy/module/_weakref/test/test_weakref.py
+++ b/pypy/module/_weakref/test/test_weakref.py
@@ -1,6 +1,9 @@
 class AppTestWeakref(object):
     spaceconfig = dict(usemodules=('_weakref',))
-                    
+
+    def setup_class(cls):
+        cls.w_runappdirect = cls.space.wrap(cls.runappdirect)
+
     def test_simple(self):
         import _weakref, gc
         class A(object):
@@ -287,6 +290,9 @@
             assert a1 is None
 
     def test_del_and_callback_and_id(self):
+        if not self.runappdirect:
+            skip("the id() doesn't work correctly in __del__ and "
+                 "callbacks before translation")
         import gc, weakref
         seen_del = []
         class A(object):


More information about the pypy-commit mailing list