Author: Armin Rigo <[email protected]>
Branch: use-gc-del-3
Changeset: r84259:88006cd809de
Date: 2016-05-06 12:10 +0200
http://bitbucket.org/pypy/pypy/changeset/88006cd809de/
Log: Restore semblance of sanity to gc.disable()/gc.enable(): now they
don't usually prevent running finalizers, but they will prevent some
explicitly-defined ones to run:
* all user __del__
* weakref callbacks
They don't prevent RPython-level things like closing files, and
also, by default, they don't prevent calls in other situations, like
cffi's 'ffi.gc()'.
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -1856,7 +1856,6 @@
('get', 'get', 3, ['__get__']),
('set', 'set', 3, ['__set__']),
('delete', 'delete', 2, ['__delete__']),
- ('userdel', 'del', 1, ['__del__']),
]
ObjSpace.BuiltinModuleTable = [
diff --git a/pypy/interpreter/executioncontext.py
b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -533,32 +533,56 @@
AsyncAction.__init__(self, space)
self.finalizers_lock_count = 0 # see pypy/module/gc
self.enabled_at_app_level = True # see pypy/module/gc
+ self.pending_with_disabled_del = None
def perform(self, executioncontext, frame):
- if self.finalizers_lock_count > 0:
- return
self._run_finalizers()
+ @jit.dont_look_inside
def _run_finalizers(self):
while True:
w_obj = self.space.finalizer_queue.next_dead()
if w_obj is None:
break
+ self._call_finalizer(w_obj)
- # Before calling the finalizers, clear the weakrefs, if any.
- w_obj.clear_all_weakrefs()
+ def gc_disabled(self, w_obj):
+ # If we're running in 'gc.disable()' mode, record w_obj in the
+ # "call me later" list and return True. Use this function
+ # from _finalize_() methods that would call app-level some
+ # things that we consider shouldn't be called in gc.disable().
+ # (The exact definition is of course a bit vague, but most
+ # importantly this includes all user-level __del__().)
+ pdd = self.pending_with_disabled_del
+ if pdd is None:
+ return False
+ else:
+ pdd.append(w_obj)
+ return True
- # Look up and call the app-level __del__, if any.
+ def _call_finalizer(self, w_obj):
+ # Before calling the finalizers, clear the weakrefs, if any.
+ w_obj.clear_all_weakrefs()
+
+ # Look up and call the app-level __del__, if any.
+ space = self.space
+ if w_obj.typedef is None:
+ w_del = None # obscure case: for WeakrefLifeline
+ else:
+ w_del = space.lookup(w_obj, '__del__')
+ if w_del is not None:
+ if self.gc_disabled(w_obj):
+ return
try:
- self.space.userdel(w_obj)
+ space.get_and_call_function(w_del, w_obj)
except Exception as e:
- report_error(self.space, e, "method __del__ of ", w_obj)
+ report_error(space, e, "method __del__ of ", w_obj)
- # Call the RPython-level _finalize_() method.
- try:
- w_obj._finalize_()
- except Exception as e:
- report_error(self.space, e, "finalizer of ", w_obj)
+ # Call the RPython-level _finalize_() method.
+ try:
+ w_obj._finalize_()
+ except Exception as e:
+ report_error(space, e, "finalizer of ", w_obj)
def report_error(space, e, where, w_obj):
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
@@ -650,6 +650,8 @@
if w_func is None:
w_func = self.getattr_from_class(space, '__del__')
if w_func is not None:
+ if self.space.user_del_action.gc_disabled(self):
+ return
space.call_function(w_func)
def descr_exit(self, space, w_type, w_value, w_tb):
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
@@ -17,6 +17,8 @@
class WeakrefLifeline(W_Root):
+ typedef = None
+
cached_weakref = None
cached_proxy = None
other_refs_weak = None
@@ -103,8 +105,7 @@
def enable_callbacks(self):
if not self.has_callbacks:
- fq = self.space.fromcache(Cache).fq
- fq.register_finalizer(self)
+ self.register_finalizer(self.space)
self.has_callbacks = True
@jit.dont_look_inside
@@ -127,39 +128,28 @@
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()
+ def _finalize_(self):
+ """This is called at the end, if enable_callbacks() was invoked.
+ It activates the callbacks.
+ """
+ if self.other_refs_weak is None:
+ return
+ #
+ # If this is set, then we're in the 'gc.disable()' mode. In that
+ # case, don't invoke the callbacks now.
+ if self.space.user_del_action.gc_disabled(self):
+ return
+ #
+ items = self.other_refs_weak.items()
+ self.other_refs_weak = None
+ 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)
# ____________________________________________________________
@@ -339,7 +329,7 @@
proxy_typedef_dict = {}
callable_proxy_typedef_dict = {}
-special_ops = {'repr': True, 'userdel': True, 'hash': True}
+special_ops = {'repr': True, 'hash': True}
for opname, _, arity, special_methods in ObjSpace.MethodTable:
if opname in special_ops or not special_methods:
diff --git a/pypy/module/gc/interp_gc.py b/pypy/module/gc/interp_gc.py
--- a/pypy/module/gc/interp_gc.py
+++ b/pypy/module/gc/interp_gc.py
@@ -38,13 +38,23 @@
return space.newbool(space.user_del_action.enabled_at_app_level)
def enable_finalizers(space):
- if space.user_del_action.finalizers_lock_count == 0:
+ uda = space.user_del_action
+ if uda.finalizers_lock_count == 0:
raise oefmt(space.w_ValueError, "finalizers are already enabled")
- space.user_del_action.finalizers_lock_count -= 1
- space.user_del_action.fire()
+ uda.finalizers_lock_count -= 1
+ if uda.finalizers_lock_count == 0:
+ pending = uda.pending_with_disabled_del
+ uda.pending_with_disabled_del = None
+ if pending is not None:
+ for i in range(len(pending)):
+ uda._call_finalizer(pending[i])
+ pending[i] = None # clear the list as we progress
def disable_finalizers(space):
- space.user_del_action.finalizers_lock_count += 1
+ uda = space.user_del_action
+ uda.finalizers_lock_count += 1
+ if uda.pending_with_disabled_del is None:
+ uda.pending_with_disabled_del = []
# ____________________________________________________________
diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py
--- a/pypy/objspace/descroperation.py
+++ b/pypy/objspace/descroperation.py
@@ -440,11 +440,6 @@
raise oefmt(space.w_TypeError,
"__hash__() should return an int or long")
- def userdel(space, w_obj):
- w_del = space.lookup(w_obj, '__del__')
- if w_del is not None:
- space.get_and_call_function(w_del, w_obj)
-
def cmp(space, w_v, w_w):
if space.is_w(w_v, w_w):
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit