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