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

Reply via email to