[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