Author: Armin Rigo <[email protected]>
Branch: use-gc-del-3
Changeset: r84217:bc676182f7b4
Date: 2016-05-05 17:03 +0200
http://bitbucket.org/pypy/pypy/changeset/bc676182f7b4/

Log:    in-progress

diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -152,9 +152,34 @@
             lifeline.clear_all_weakrefs()
 
     def _finalize_(self):
-        """The RPython-level finalizer.  
+        """The RPython-level finalizer.
 
-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"""
+        By default, it is *not called*.  See self.register_finalizer().
+        Be ready to handle the case where the object is only half
+        initialized.
+        """
+
+    def register_finalizer(self, space):
+        """Register a finalizer for this object, so that
+        self._finalize_() will be called.  You must call this method at
+        most once.  Be ready to handle in _finalize_() the case where
+        the object is half-initialized, even if you only call
+        self.register_finalizer() at the end of the initialization.
+        This is because there are cases where the finalizer is already
+        registered before: if the user makes an app-level subclass with
+        a __del__.  (In that case only, self.register_finalizer() does
+        nothing, because the finalizer is already registered in
+        allocate_instance().)
+        """
+        if self.user_overridden_class and self.getclass(space).hasuserdel:
+            # already registered by space.allocate_instance()
+            if not we_are_translated():
+                assert space.finalizer_queue._already_registered(self)
+        else:
+            if not we_are_translated():
+                # does not make sense if _finalize_ is not overridden
+                assert self._finalize_.im_func is not W_Root._finalize_.im_func
+            space.finalizer_queue.register_finalizer(self)
 
     # hooks that the mapdict implementations needs:
     def _get_mapdict_map(self):
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -103,26 +103,21 @@
 # we need two subclasses of the app-level type, one to add mapdict, and then 
one
 # to add del to not slow down the GC.
 
-def get_unique_interplevel_subclass(space, cls, needsdel=False):
+def get_unique_interplevel_subclass(space, cls):
     "NOT_RPYTHON: initialization-time only"
-    if hasattr(cls, '__del__') and getattr(cls, "handle_del_manually", False):
-        needsdel = False
     assert cls.typedef.acceptable_as_base_class
-    key = space, cls, needsdel
+    key = space, cls
     try:
         return _subclass_cache[key]
     except KeyError:
-        # XXX can save a class if cls already has a __del__
-        if needsdel:
-            cls = get_unique_interplevel_subclass(space, cls, False)
-        subcls = _getusercls(space, cls, needsdel)
+        subcls = _getusercls(space, cls)
         assert key not in _subclass_cache
         _subclass_cache[key] = subcls
         return subcls
 get_unique_interplevel_subclass._annspecialcase_ = "specialize:memo"
 _subclass_cache = {}
 
-def _getusercls(space, cls, wants_del, reallywantdict=False):
+def _getusercls(space, cls, reallywantdict=False):
     from rpython.rlib import objectmodel
     from pypy.objspace.std.objectobject import W_ObjectObject
     from pypy.module.__builtin__.interp_classobj import W_InstanceObject
@@ -132,11 +127,10 @@
     typedef = cls.typedef
     name = cls.__name__ + "User"
 
-    mixins_needed = []
     if cls is W_ObjectObject or cls is W_InstanceObject:
-        mixins_needed.append(_make_storage_mixin_size_n())
+        base_mixin = _make_storage_mixin_size_n()
     else:
-        mixins_needed.append(MapdictStorageMixin)
+        base_mixin = MapdictStorageMixin
     copy_methods = [BaseUserClassMapdict]
     if reallywantdict or not typedef.hasdict:
         # the type has no dict, mapdict to provide the dict
@@ -147,44 +141,12 @@
         # support
         copy_methods.append(MapdictWeakrefSupport)
         name += "Weakrefable"
-    if wants_del:
-        # This subclass comes with an app-level __del__.  To handle
-        # it, we make an RPython-level __del__ method.  This
-        # RPython-level method is called directly by the GC and it
-        # cannot do random things (calling the app-level __del__ would
-        # be "random things").  So instead, we just call here
-        # enqueue_for_destruction(), and the app-level __del__ will be
-        # called later at a safe point (typically between bytecodes).
-        # If there is also an inherited RPython-level __del__, it is
-        # called afterwards---not immediately!  This base
-        # RPython-level __del__ is supposed to run only when the
-        # object is not reachable any more.  NOTE: it doesn't fully
-        # work: see issue #2287.
-        name += "Del"
-        parent_destructor = getattr(cls, '__del__', None)
-        def call_parent_del(self):
-            assert isinstance(self, subcls)
-            parent_destructor(self)
-        def call_applevel_del(self):
-            assert isinstance(self, subcls)
-            space.userdel(self)
-        class Proto(object):
-            def __del__(self):
-                self.clear_all_weakrefs()
-                self.enqueue_for_destruction(space, call_applevel_del,
-                                             'method __del__ of ')
-                if parent_destructor is not None:
-                    self.enqueue_for_destruction(space, call_parent_del,
-                                                 'internal destructor of ')
-        mixins_needed.append(Proto)
 
     class subcls(cls):
         user_overridden_class = True
-        for base in mixins_needed:
-            objectmodel.import_from_mixin(base)
+        objectmodel.import_from_mixin(base_mixin)
     for copycls in copy_methods:
         _copy_methods(copycls, subcls)
-    del subcls.base
     subcls.__name__ = name
     return subcls
 
diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py
--- a/pypy/objspace/std/objspace.py
+++ b/pypy/objspace/std/objspace.py
@@ -357,11 +357,12 @@
             if cls.typedef.applevel_subclasses_base is not None:
                 cls = cls.typedef.applevel_subclasses_base
             #
-            subcls = get_unique_interplevel_subclass(
-                    self, cls, w_subtype.needsdel)
+            subcls = get_unique_interplevel_subclass(self, cls)
             instance = instantiate(subcls)
             assert isinstance(instance, cls)
             instance.user_setup(self, w_subtype)
+            if w_subtype.hasuserdel:
+                space.finalizer_queue.register_finalizer(instance)
         else:
             raise oefmt(self.w_TypeError,
                         "%N.__new__(%N): only for the type %N",
diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py
--- a/pypy/objspace/std/typeobject.py
+++ b/pypy/objspace/std/typeobject.py
@@ -132,7 +132,7 @@
                           "flag_sequence_bug_compat",
                           "flag_map_or_seq",    # '?' or 'M' or 'S'
                           "compares_by_identity_status?",
-                          'needsdel',
+                          'hasuserdel',
                           'weakrefable',
                           'hasdict',
                           'layout',
@@ -160,7 +160,7 @@
         w_self.bases_w = bases_w
         w_self.dict_w = dict_w
         w_self.hasdict = False
-        w_self.needsdel = False
+        w_self.hasuserdel = False
         w_self.weakrefable = False
         w_self.w_doc = space.w_None
         w_self.weak_subclasses = []
@@ -289,7 +289,7 @@
     # compute a tuple that fully describes the instance layout
     def get_full_instance_layout(w_self):
         layout = w_self.layout
-        return (layout, w_self.hasdict, w_self.needsdel, w_self.weakrefable)
+        return (layout, w_self.hasdict, w_self.weakrefable)
 
     def compute_default_mro(w_self):
         return compute_C3_mro(w_self.space, w_self)
@@ -986,7 +986,7 @@
             hasoldstylebase = True
             continue
         w_self.hasdict = w_self.hasdict or w_base.hasdict
-        w_self.needsdel = w_self.needsdel or w_base.needsdel
+        w_self.hasuserdel = w_self.hasuserdel or w_base.hasuserdel
         w_self.weakrefable = w_self.weakrefable or w_base.weakrefable
     return hasoldstylebase
 
@@ -1028,7 +1028,7 @@
     if wantweakref:
         create_weakref_slot(w_self)
     if '__del__' in dict_w:
-        w_self.needsdel = True
+        w_self.hasuserdel = True
     #
     if index_next_extra_slot == base_layout.nslots and not force_new_layout:
         return base_layout
diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py
--- a/rpython/rlib/rgc.py
+++ b/rpython/rlib/rgc.py
@@ -428,9 +428,11 @@
         self._weakrefs = set()
         self._queue = collections.deque()
 
+    def _already_registered(self, obj):
+        return hasattr(obj, '__enable_del_for_id')
+
     def _untranslated_register_finalizer(self, obj):
-        if hasattr(obj, '__enable_del_for_id'):
-            return    # already called
+        assert not self._already_registered(obj)
 
         if not hasattr(self, '_queue'):
             self._reset()
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to