Author: Armin Rigo <[email protected]>
Branch: gc-del
Changeset: r63607:f21d31bd52cf
Date: 2013-04-25 16:49 +0200
http://bitbucket.org/pypy/pypy/changeset/f21d31bd52cf/
Log: Generalize rgc.register_finalizer() into an API for any W_Root
object.
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -154,25 +154,38 @@
self.delweakref()
lifeline.clear_all_weakrefs()
- __already_enqueued_for_destruction = ()
+ def register_finalizer(self):
+ """Register the fact that this object needs finalization.
+ Can be called several times on the same object with no ill effect."""
+ from rpython.rlib import rgc
+ rgc.register_finalizer(self._invoke_finalizer)
- def enqueue_for_destruction(self, space, callback, descrname):
- """Put the object in the destructor queue of the space.
- At a later, safe point in time, UserDelAction will call
- callback(self). If that raises OperationError, prints it
- to stderr with the descrname string.
+ def _invoke_finalizer(self):
+ # The call-back from rgc.register_finalizer(), cannot be overridden
+ self.invoke_finalizer()
- Note that 'callback' will usually need to start with:
- assert isinstance(self, W_SpecificClass)
+ def invoke_finalizer(self):
+ raise NotImplementedError
+
+ def finalizer_perform(self, space, descrname, callback, *args):
+ """For use in invoke_finalizer(). First check if we're called
+ from the random execution of a __del__ or from UserDelAction,
+ and raise _FinalizeLater in the former case. Then invoke
+ callback(*args). If that raises OperationError, prints it to
+ stderr with the descrname string. The format is:
+
+ Exception XxxError: "error message" in %s%s ignored
+
+ The first %s is descrname (which should thus end with a space
+ character), and the second %s is repr(self).
"""
- # this function always resurect the object, so when
- # running on top of CPython we must manually ensure that
- # we enqueue it only once
- if not we_are_translated():
- if callback in self.__already_enqueued_for_destruction:
- return
- self.__already_enqueued_for_destruction += (callback,)
- space.user_del_action.register_callback(self, callback, descrname)
+ space.user_del_action.must_be_between_bytecodes()
+ try:
+ callback(*args)
+ except OperationError, e:
+ e.write_unraisable(space, descrname, self)
+ e.clear(space) # break up reference cycles
+ finalizer_perform._annspecialcase_ = 'specialize:arg(3)'
# hooks that the mapdict implementations needs:
def _get_mapdict_map(self):
diff --git a/pypy/interpreter/executioncontext.py
b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -435,41 +435,31 @@
class UserDelAction(AsyncAction):
- """An action that invokes all pending app-level __del__() method.
+ """An action that invokes all pending finalizers.
This is done as an action instead of immediately when the
- interp-level __del__() is invoked, because the latter can occur more
+ interp-level finalizer is invoked, because the latter can occur more
or less anywhere in the middle of code that might not be happy with
random app-level code mutating data structures under its feet.
"""
def __init__(self, space):
AsyncAction.__init__(self, space)
- self.dying_objects = []
- self.finalizers_lock_count = 0
- self.enabled_at_app_level = True
+ self.in_user_del_action = False
- def register_callback(self, w_obj, callback, descrname):
- self.dying_objects.append((w_obj, callback, descrname))
- self.fire()
+ def must_be_between_bytecodes(self):
+ if not self.in_user_del_action:
+ from rpython.rlib import rgc
+ self.fire()
+ rgc.finalize_later()
def perform(self, executioncontext, frame):
- if self.finalizers_lock_count > 0:
- return
- # Each call to perform() first grabs the self.dying_objects
- # and replaces it with an empty list. We do this to try to
- # avoid too deep recursions of the kind of __del__ being called
- # while in the middle of another __del__ call.
- pending = self.dying_objects
- self.dying_objects = []
- space = self.space
- for i in range(len(pending)):
- w_obj, callback, descrname = pending[i]
- pending[i] = (None, None, None)
+ from rpython.rlib import rgc
+ if not self.in_user_del_action:
+ self.in_user_del_action = True
try:
- callback(w_obj)
- except OperationError, e:
- e.write_unraisable(space, descrname, w_obj)
- e.clear(space) # break up reference cycles
+ rgc.progress_through_finalizer_queue()
+ finally:
+ self.in_user_del_action = False
class FrameTraceAction(AsyncAction):
"""An action that calls the local trace functions (w_f_trace)."""
diff --git a/pypy/module/__builtin__/interp_classobj.py
b/pypy/module/__builtin__/interp_classobj.py
--- a/pypy/module/__builtin__/interp_classobj.py
+++ b/pypy/module/__builtin__/interp_classobj.py
@@ -6,7 +6,7 @@
from pypy.interpreter.typedef import GetSetProperty, descr_get_dict,
descr_set_dict
from rpython.rlib.objectmodel import compute_identity_hash
from rpython.rlib.debug import make_sure_not_resized
-from rpython.rlib import rgc, jit
+from rpython.rlib import jit
def raise_type_err(space, argument, expected, w_obj):
@@ -313,7 +313,7 @@
assert isinstance(w_class, W_ClassObject)
self.w_class = w_class
if self.getattr_from_class(space, '__del__') is not None:
- rgc.register_finalizer(self.finalizer)
+ self.register_finalizer()
def user_setup(self, space, w_subtype):
self.space = space
@@ -385,7 +385,7 @@
self.set_oldstyle_class(space, w_value)
return
if name == '__del__':
- rgc.register_finalizer(self.finalizer)
+ self.register_finalizer()
if w_meth is not None:
space.call_function(w_meth, w_name, w_value)
else:
@@ -688,13 +688,14 @@
space.wrap("instance has no next() method"))
return space.call_function(w_func)
- def finalizer(self):
+ def invoke_finalizer(self):
space = self.space
w_func = self.getdictvalue(space, '__del__')
if w_func is None:
w_func = self.getattr_from_class(space, '__del__')
if w_func is not None:
- space.call_function(w_func)
+ self.finalizer_perform(self.space, "__del__ of ",
+ space.call_function, w_func)
def descr_exit(self, space, w_type, w_value, w_tb):
w_func = self.getattr(space, '__exit__', False)
diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py
--- a/rpython/rlib/rgc.py
+++ b/rpython/rlib/rgc.py
@@ -290,7 +290,7 @@
gc_supports_finalize_later = CDefinedIntSymbolic('GC_SUPPORTS_FINALIZE_LATER',
default=1)
def finalize_later():
- if gc_supports_finalize_later:
+ if not we_are_translated() or gc_supports_finalize_later:
raise _FinalizeLater
def register_finalizer(method):
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit