[pypy-svn] r56085 - in pypy/branch/async-del/pypy: interpreter interpreter/test module/gc
arigo at codespeak.net
arigo at codespeak.net
Thu Jun 26 11:26:12 CEST 2008
Author: arigo
Date: Thu Jun 26 11:26:11 2008
New Revision: 56085
Modified:
pypy/branch/async-del/pypy/interpreter/baseobjspace.py
pypy/branch/async-del/pypy/interpreter/executioncontext.py
pypy/branch/async-del/pypy/interpreter/test/test_typedef.py
pypy/branch/async-del/pypy/interpreter/typedef.py
pypy/branch/async-del/pypy/module/gc/interp_gc.py
Log:
Attempting to fix the issue by delaying calls to app-level dels.
Modified: pypy/branch/async-del/pypy/interpreter/baseobjspace.py
==============================================================================
--- pypy/branch/async-del/pypy/interpreter/baseobjspace.py (original)
+++ pypy/branch/async-del/pypy/interpreter/baseobjspace.py Thu Jun 26 11:26:11 2008
@@ -1,4 +1,4 @@
-from pypy.interpreter.executioncontext import ExecutionContext
+from pypy.interpreter.executioncontext import ExecutionContext, UserDelAction
from pypy.interpreter.error import OperationError
from pypy.interpreter.argument import Arguments, ArgumentsFromValuestack
from pypy.interpreter.pycompiler import CPythonCompiler, PythonAstCompiler
@@ -137,6 +137,9 @@
self.setweakref(lifeline.space, None)
lifeline.clear_all_weakrefs()
+ def _call_builtin_destructor(self):
+ pass # method overridden in typedef.py
+
class Wrappable(W_Root):
"""A subclass of Wrappable is an internal, interpreter-level class
@@ -218,7 +221,8 @@
import pypy.interpreter.nestedscope # register *_DEREF bytecodes
self.interned_strings = {}
- self.pending_actions = []
+ self.user_del_action = UserDelAction(self)
+ self.pending_actions = [self.user_del_action]
self.setoptions(**kw)
# if self.config.objspace.logbytecodes:
Modified: pypy/branch/async-del/pypy/interpreter/executioncontext.py
==============================================================================
--- pypy/branch/async-del/pypy/interpreter/executioncontext.py (original)
+++ pypy/branch/async-del/pypy/interpreter/executioncontext.py Thu Jun 26 11:26:11 2008
@@ -118,9 +118,9 @@
def _do_bytecode_trace(self, frame):
if self.ticker < 0:
+ self.ticker = self.space.sys.checkinterval
Action.perform_actions(self.space.pending_actions)
Action.perform_actions(self.pending_actions)
- self.ticker = self.space.sys.checkinterval
if frame.w_f_trace is None or self.is_tracing:
return
code = frame.pycode
@@ -278,3 +278,42 @@
raise Exception("ExecutionContext instances should not be seen during"
" translation. Now is a good time to inspect the"
" traceback and see where this one comes from :-)")
+
+
+class UserDelAction(Action):
+ """An action that invokes all pending app-level __del__() method.
+ This is done as an action instead of immediately when the
+ interp-level __del__() 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):
+ self.space = space
+ self.dying_objects_w = []
+
+ def register_dying_object(self, w_obj):
+ self.dying_objects_w.append(w_obj)
+ # XXX should force the action to occur as soon as possible.
+ # "space.getexectuion().ticker = 0" is both expensive and not
+ # exactly what we need because it's ok if the action occurs
+ # in any thread
+
+ def perform(self):
+ # Each call to perform() first grabs the self.dying_objects_w
+ # 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_w = self.dying_objects_w
+ if len(pending_w) == 0:
+ return # shortcut
+ self.dying_objects_w = []
+ space = self.space
+ for w_obj in pending_w:
+ try:
+ space.userdel(w_obj)
+ except OperationError, e:
+ e.write_unraisable(space, 'method __del__ of ', w_obj)
+ e.clear(space) # break up reference cycles
+ # finally, this calls the interp-level destructor for the
+ # cases where there is both an app-level and a built-in __del__.
+ w_obj._call_builtin_destructor()
Modified: pypy/branch/async-del/pypy/interpreter/test/test_typedef.py
==============================================================================
--- pypy/branch/async-del/pypy/interpreter/test/test_typedef.py (original)
+++ pypy/branch/async-del/pypy/interpreter/test/test_typedef.py Thu Jun 26 11:26:11 2008
@@ -1,4 +1,5 @@
from pypy.interpreter import typedef
+from pypy.tool.udir import udir
# this test isn't so much to test that the objspace interface *works*
# -- it's more to test that it's *there*
@@ -130,3 +131,27 @@
for cls, set in subclasses.items():
assert len(set) <= 6, "%s has %d subclasses:\n%r" % (
cls, len(set), [subcls.__name__ for subcls in set])
+
+
+class AppTestTypeDef:
+
+ def setup_class(cls):
+ path = udir.join('AppTestTypeDef.txt')
+ path.write('hello world\n')
+ cls.w_path = cls.space.wrap(str(path))
+
+ def test_destructor(self):
+ import gc, os
+ seen = []
+ class MyFile(file):
+ def __del__(self):
+ seen.append(10)
+ seen.append(os.lseek(self.fileno(), 2, 0))
+ f = MyFile(self.path, 'r')
+ fd = f.fileno()
+ seen.append(os.lseek(fd, 5, 0))
+ del f
+ gc.collect(); gc.collect(); gc.collect()
+ lst = seen[:]
+ assert lst == [5, 10, 2]
+ raises(OSError, os.lseek, fd, 7, 0)
Modified: pypy/branch/async-del/pypy/interpreter/typedef.py
==============================================================================
--- pypy/branch/async-del/pypy/interpreter/typedef.py (original)
+++ pypy/branch/async-del/pypy/interpreter/typedef.py Thu Jun 26 11:26:11 2008
@@ -9,7 +9,7 @@
DescrMismatch
from pypy.interpreter.error import OperationError
from pypy.tool.sourcetools import compile2, func_with_new_name
-from pypy.rlib.objectmodel import instantiate
+from pypy.rlib.objectmodel import instantiate, we_are_translated
from pypy.rlib.rarithmetic import intmask
class TypeDef:
@@ -247,12 +247,27 @@
add(Proto)
if "del" in features:
- parent_destructor = getattr(supercls, '__del__', None)
class Proto(object):
+ _del_was_called = False
def __del__(self):
- call_user_destructor(self.space, self)
- if parent_destructor is not None:
- parent_destructor(self)
+ # the logic below always resurect the objects, so when
+ # running on top of CPython we must manually ensure that
+ # we do it only once
+ if not we_are_translated():
+ if self._del_was_called:
+ return
+ self._del_was_called = True
+ self.clear_all_weakrefs()
+ self.space.user_del_action.register_dying_object(self)
+ # if the base class needs its own interp-level __del__,
+ # we override the _call_builtin_destructor() method to invoke it
+ # after the app-level destructor.
+ parent_destructor = getattr(supercls, '__del__', None)
+ if parent_destructor is not None:
+ def _call_builtin_destructor(self):
+ parent_destructor(self)
+ Proto._call_builtin_destructor = _call_builtin_destructor
+
add(Proto)
if "slots" in features:
@@ -323,15 +338,6 @@
return w_dict
check_new_dictionary._dont_inline_ = True
-def call_user_destructor(space, w_self):
- w_self.clear_all_weakrefs()
- try:
- space.userdel(w_self)
- except OperationError, e:
- e.write_unraisable(space, 'method __del__ of ', w_self)
- e.clear(space) # break up reference cycles
-call_user_destructor._dont_inline_ = True
-
# ____________________________________________________________
def make_descr_typecheck_wrapper(func, extraargs=(), cls=None):
Modified: pypy/branch/async-del/pypy/module/gc/interp_gc.py
==============================================================================
--- pypy/branch/async-del/pypy/module/gc/interp_gc.py (original)
+++ pypy/branch/async-del/pypy/module/gc/interp_gc.py Thu Jun 26 11:26:11 2008
@@ -5,6 +5,7 @@
def collect(space):
"Run a full collection."
rgc.collect()
+ space.user_del_action.perform()
return space.wrap(0)
collect.unwrap_spec = [ObjSpace]
More information about the Pypy-commit
mailing list