[pypy-svn] r56090 - in pypy/branch/async-del/pypy: interpreter interpreter/test module/_file

arigo at codespeak.net arigo at codespeak.net
Thu Jun 26 21:22:27 CEST 2008


Author: arigo
Date: Thu Jun 26 21:22:26 2008
New Revision: 56090

Modified:
   pypy/branch/async-del/pypy/interpreter/baseobjspace.py
   pypy/branch/async-del/pypy/interpreter/executioncontext.py
   pypy/branch/async-del/pypy/interpreter/miscutils.py
   pypy/branch/async-del/pypy/interpreter/test/test_executioncontext.py
   pypy/branch/async-del/pypy/interpreter/test/test_pyframe.py
   pypy/branch/async-del/pypy/module/_file/interp_stream.py
Log:
Still in-progress.  Added some tests.


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 21:22:26 2008
@@ -1,5 +1,5 @@
-from pypy.interpreter.executioncontext import ExecutionContext, UserDelAction
-from pypy.interpreter.executioncontext import ActionFlag
+from pypy.interpreter.executioncontext import ExecutionContext, ActionFlag
+from pypy.interpreter.executioncontext import UserDelAction, FrameTraceAction
 from pypy.interpreter.error import OperationError
 from pypy.interpreter.argument import Arguments, ArgumentsFromValuestack
 from pypy.interpreter.pycompiler import CPythonCompiler, PythonAstCompiler
@@ -224,7 +224,9 @@
         self.interned_strings = {}
         self.actionflag = ActionFlag()    # changed by the signal module
         self.user_del_action = UserDelAction(self)
-        self.register_async_action(self.user_del_action)
+        self.frame_trace_action = FrameTraceAction(self)
+        self.actionflag.register_action(self.user_del_action)
+        self.actionflag.register_action(self.frame_trace_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 21:22:26 2008
@@ -1,5 +1,5 @@
 import sys
-from pypy.interpreter.miscutils import Stack, Action
+from pypy.interpreter.miscutils import Stack
 from pypy.interpreter.error import OperationError
 from pypy.rlib.rarithmetic import LONG_BIT
 from pypy.rlib.unroll import unrolling_iterable
@@ -19,6 +19,9 @@
     def __init__(self, space):
         self.space = space
         self.framestack = new_framestack()
+        # tracing: space.frame_trace_action.fire() must be called to ensure
+        # that tracing occurs whenever self.w_tracefunc or self.is_tracing
+        # is modified.
         self.w_tracefunc = None
         self.is_tracing = 0
         self.compiler = space.createcompiler()
@@ -61,6 +64,7 @@
             ec.profilefunc = self.profilefunc
             ec.w_profilefuncarg = self.w_profilefuncarg
             ec.is_tracing = self.is_tracing
+            ec.space.frame_trace_action.fire()
 
         def leave(self, ec):
             self.framestack = ec.framestack
@@ -116,56 +120,9 @@
             ticker += 1
             actionflag.set(ticker)
         if ticker & actionflag.interesting_bits:  # fast check
-            actionflag.action_dispatcher()            # slow path
+            actionflag.action_dispatcher(self)        # slow path
     bytecode_trace._always_inline_ = True
 
-    def _do_bytecode_trace(self, frame):
-        ./.
-        if frame.w_f_trace is None or self.is_tracing:
-            return
-        code = frame.pycode
-        if frame.instr_lb <= frame.last_instr < frame.instr_ub:
-            if frame.last_instr <= frame.instr_prev:
-                # We jumped backwards in the same line.
-                self._trace(frame, 'line', self.space.w_None)
-        else:
-            size = len(code.co_lnotab) / 2
-            addr = 0
-            line = code.co_firstlineno
-            p = 0
-            lineno = code.co_lnotab
-            while size > 0:
-                c = ord(lineno[p])
-                if (addr + c) > frame.last_instr:
-                    break
-                addr += c
-                if c:
-                    frame.instr_lb = addr
-
-                line += ord(lineno[p + 1])
-                p += 2
-                size -= 1
-
-            if size > 0:
-                while True:
-                    size -= 1
-                    if size < 0:
-                        break
-                    addr += ord(lineno[p])
-                    if ord(lineno[p + 1]):
-                        break
-                    p += 2
-                frame.instr_ub = addr
-            else:
-                frame.instr_ub = sys.maxint
-
-            if frame.instr_lb == frame.last_instr: # At start of line!
-                frame.f_lineno = line
-                self._trace(frame, 'line', self.space.w_None)
-
-        frame.instr_prev = frame.last_instr
-    _do_bytecode_trace._dont_inline_ = True
-
     def exception_trace(self, frame, operationerr):
         "Trace function called upon OperationError."
         operationerr.record_interpreter_traceback()
@@ -188,6 +145,7 @@
             self.w_tracefunc = None
         else:
             self.w_tracefunc = w_func
+            self.space.frame_trace_action.fire()
 
     def setprofile(self, w_func):
         """Set the global trace function."""
@@ -208,6 +166,7 @@
         is_tracing = self.is_tracing
         self.is_tracing = 0
         try:
+            self.space.frame_trace_action.fire()
             return self.space.call(w_func, w_args)
         finally:
             self.is_tracing = is_tracing
@@ -271,10 +230,6 @@
                 frame.last_exception = last_exception
                 self.is_tracing -= 1
 
-    def add_pending_action(self, action):
-        self.pending_actions.append(action)
-        self.ticker = 0
-
     def _freeze_(self):
         raise Exception("ExecutionContext instances should not be seen during"
                         " translation.  Now is a good time to inspect the"
@@ -328,6 +283,7 @@
             assert action.bitmask == self.BYTECODE_COUNTER_OVERFLOW_BIT
             self._periodic_actions.append(action)
             self.has_bytecode_counter = True
+            self.force_tick_counter()
         else:
             self._nonperiodic_actions.append((action, action.bitmask))
         self._rebuild_action_dispatcher()
@@ -338,7 +294,11 @@
         elif interval > self.CHECK_INTERVAL_MAX:
             interval = self.CHECK_INTERVAL_MAX
         space.sys.checkinterval = interval
-        # force the tick counter to a correct value
+        self.force_tick_counter()
+
+    def force_tick_counter(self):
+        # force the tick counter to a valid value -- this actually forces
+        # it to reach BYTECODE_COUNTER_OVERFLOW_BIT at the next opcode.
         ticker = self.get()
         ticker |= self.BYTECODE_COUNTER_MASK
         self.set(ticker)
@@ -361,14 +321,14 @@
                     ticker -= ec.space.sys.checkinterval
                     self.set(ticker)
                     for action in periodic_actions:
-                        action.perform()
+                        action.perform(ec)
 
             # nonperiodic actions
             for action, bitmask in nonperiodic_actions:
                 ticker = self.get()
                 if ticker & bitmask:
                     self.set(ticker & ~ bitmask)
-                    action.perform()
+                    action.perform(ec)
 
         action_dispatcher._dont_inline_ = True
         self.action_dispatcher = action_dispatcher
@@ -382,8 +342,8 @@
 
     # The acceptable range of values for sys.checkinterval, so that
     # the bytecode_counter fits in 20 bits
-    CHECK_INTERVAL_MIN   = 1
-    CHECK_INTERVAL_MAX   = BYTECODE_COUNTER_OVERFLOW_BIT
+    CHECK_INTERVAL_MIN = 1
+    CHECK_INTERVAL_MAX = BYTECODE_COUNTER_OVERFLOW_BIT
 
 
 class AsyncAction(object):
@@ -393,7 +353,15 @@
     """
     bitmask = 'auto'
 
-    def perform(self):
+    def __init__(self, space):
+        self.space = space
+
+    def fire(self):
+        """Request for the action to be run before the next opcode.
+        The action must have been registered at space initalization time."""
+        self.space.actionflag.fire(self)
+
+    def perform(self, executioncontext):
         """To be overridden."""
 
 
@@ -404,8 +372,7 @@
     bitmask = ActionFlag.BYTECODE_COUNTER_OVERFLOW_BIT
 
 
-class UserDelAction(Action):
-    ./.
+class UserDelAction(AsyncAction):
     """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
@@ -414,24 +381,19 @@
     """
 
     def __init__(self, space):
-        self.space = space
+        AsyncAction.__init__(self, 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
+        self.fire()
 
-    def perform(self):
+    def perform(self, executioncontext):
         # 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:
@@ -443,3 +405,54 @@
             # 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()
+
+
+class FrameTraceAction(AsyncAction):
+    """An action that calls the local trace functions (w_f_trace)."""
+
+    def perform(self, executioncontext):
+        frame = executioncontext.framestack.top()
+        if frame.w_f_trace is None or executioncontext.is_tracing:
+            return
+        code = frame.pycode
+        if frame.instr_lb <= frame.last_instr < frame.instr_ub:
+            if frame.last_instr <= frame.instr_prev:
+                # We jumped backwards in the same line.
+                executioncontext._trace(frame, 'line', self.space.w_None)
+        else:
+            size = len(code.co_lnotab) / 2
+            addr = 0
+            line = code.co_firstlineno
+            p = 0
+            lineno = code.co_lnotab
+            while size > 0:
+                c = ord(lineno[p])
+                if (addr + c) > frame.last_instr:
+                    break
+                addr += c
+                if c:
+                    frame.instr_lb = addr
+
+                line += ord(lineno[p + 1])
+                p += 2
+                size -= 1
+
+            if size > 0:
+                while True:
+                    size -= 1
+                    if size < 0:
+                        break
+                    addr += ord(lineno[p])
+                    if ord(lineno[p + 1]):
+                        break
+                    p += 2
+                frame.instr_ub = addr
+            else:
+                frame.instr_ub = sys.maxint
+
+            if frame.instr_lb == frame.last_instr: # At start of line!
+                frame.f_lineno = line
+                executioncontext._trace(frame, 'line', self.space.w_None)
+
+        frame.instr_prev = frame.last_instr
+        self.space.frame_trace_action.fire()     # continue tracing

Modified: pypy/branch/async-del/pypy/interpreter/miscutils.py
==============================================================================
--- pypy/branch/async-del/pypy/interpreter/miscutils.py	(original)
+++ pypy/branch/async-del/pypy/interpreter/miscutils.py	Thu Jun 26 21:22:26 2008
@@ -167,26 +167,3 @@
 
     def getmainthreadvalue(self):
         return self._value
-
-
-class Action(object):
-    """Abstract base class for actions that must be performed regularly,
-    every Nth bytecode (as selected by sys.setcheckinterval())."""
-
-    # set repeat to True for actions that must be kept around and
-    # re-performed regularly
-    repeat = False
-
-    def perform(self):
-        """To be overridden."""
-
-    def perform_actions(actionlist):
-        i = 0
-        while i < len(actionlist):
-            a = actionlist[i]
-            if a.repeat:
-                i += 1     # keep action
-            else:
-                del actionlist[i]
-            a.perform()
-    perform_actions = staticmethod(perform_actions)

Modified: pypy/branch/async-del/pypy/interpreter/test/test_executioncontext.py
==============================================================================
--- pypy/branch/async-del/pypy/interpreter/test/test_executioncontext.py	(original)
+++ pypy/branch/async-del/pypy/interpreter/test/test_executioncontext.py	Thu Jun 26 21:22:26 2008
@@ -1,39 +1,65 @@
 import py
-from pypy.interpreter import miscutils
+from pypy.interpreter import executioncontext
+
+
+class Finished(Exception):
+    pass
 
 
 class TestExecutionContext:
 
     def test_action(self):
-        class Finished(Exception):
-            pass
 
-        class DemoAction(miscutils.Action):
-            def __init__(self, repeat):
-                self.repeat = repeat
-                self.counter = 0
+        class DemoAction(executioncontext.AsyncAction):
+            counter = 0
             def perform(self):
                 self.counter += 1
                 if self.counter == 10:
                     raise Finished
 
-        a1 = DemoAction(False)
-        a2 = DemoAction(True)
-        a3 = DemoAction(False)
+        space = self.space
+        a1 = DemoAction(space)
+        space.actionflag.register_action(a1)
+        for i in range(20):
+            # assert does not raise:
+            space.appexec([], """():
+                n = 5
+                return n + 2
+            """)
+        try:
+            for i in range(20):
+                a1.fire()
+                space.appexec([], """():
+                    n = 5
+                    return n + 2
+                """)
+                assert a1.counter == i + 1
+        except Finished:
+            pass
+        assert i == 9
+
+    def test_periodic_action(self):
+
+        class DemoAction(executioncontext.PeriodicAsyncAction):
+            counter = 0
+            def perform(self):
+                self.counter += 1
+                print '->', self.counter
+                if self.counter == 3:
+                    raise Finished
 
         space = self.space
-        space.pending_actions.append(a1)
-        space.getexecutioncontext().add_pending_action(a2)
-        space.getexecutioncontext().add_pending_action(a3)
-
-        py.test.raises(Finished, space.appexec, [], """():
-            n = 50000
-            while n > 0:
-                n -= 1
-        """)
-        assert a1.counter == 1
-        assert a2.counter == 10
-        assert a3.counter == 1
+        a2 = DemoAction(space)
+        space.actionflag.register_action(a2)
+        try:
+            for i in range(500):
+                space.appexec([], """():
+                    n = 5
+                    return n + 2
+                """)
+        except Finished:
+            pass
+        assert space.sys.checkinterval / 10 < i < space.sys.checkinterval * 3
 
     def test_llprofile(self):
         l = []

Modified: pypy/branch/async-del/pypy/interpreter/test/test_pyframe.py
==============================================================================
--- pypy/branch/async-del/pypy/interpreter/test/test_pyframe.py	(original)
+++ pypy/branch/async-del/pypy/interpreter/test/test_pyframe.py	Thu Jun 26 21:22:26 2008
@@ -73,7 +73,54 @@
             raise OuterException
         except:
             g(sys.exc_info())
-        
+
+    def test_trace_basic(self):
+        import sys
+        l = []
+        class Tracer:
+            def __init__(self, i):
+                self.i = i
+            def trace(self, frame, event, arg):
+                l.append((self.i, frame.f_code.co_name, event, arg))
+                if frame.f_code.co_name == 'g2':
+                    return None    # don't trace g2
+                return Tracer(self.i+1).trace
+        def g3(n):
+            n -= 5
+            return n
+        def g2(n):
+            n += g3(2)
+            n += g3(7)
+            return n
+        def g(n):
+            n += g2(3)
+            return n
+        def f(n):
+            n = g(n)
+            return n * 7
+        sys.settrace(Tracer(0).trace)
+        x = f(4)
+        sys.settrace(None)
+        assert x == 42
+        print l
+        assert l == [(0, 'f', 'call', None),
+                     (1, 'f', 'line', None),
+                         (0, 'g', 'call', None),
+                         (1, 'g', 'line', None),
+                             (0, 'g2', 'call', None),
+                                 (0, 'g3', 'call', None),
+                                 (1, 'g3', 'line', None),
+                                 (2, 'g3', 'line', None),
+                                 (3, 'g3', 'return', -3),
+                                 (0, 'g3', 'call', None),
+                                 (1, 'g3', 'line', None),
+                                 (2, 'g3', 'line', None),
+                                 (3, 'g3', 'return', 2),
+                         (2, 'g', 'line', None),
+                         (3, 'g', 'return', 6),
+                     (2, 'f', 'line', None),
+                     (3, 'f', 'return', 42)]
+
     def test_trace_exc(self):
         import sys
         l = []

Modified: pypy/branch/async-del/pypy/module/_file/interp_stream.py
==============================================================================
--- pypy/branch/async-del/pypy/module/_file/interp_stream.py	(original)
+++ pypy/branch/async-del/pypy/module/_file/interp_stream.py	Thu Jun 26 21:22:26 2008
@@ -7,7 +7,6 @@
 from pypy.interpreter.baseobjspace import Wrappable
 from pypy.interpreter.typedef import TypeDef
 from pypy.interpreter.gateway import interp2app
-from pypy.interpreter.miscutils import Action
 
 import os
 
@@ -25,14 +24,6 @@
 def wrap_oserror_as_ioerror(space, e):
     assert isinstance(e, OSError)
     errno = e.errno
-    if errno == EINTR:
-        # A signal was sent to the process and interupted
-        # a systemcall. We want to trigger running of
-        # any installed interrupt handlers.
-        # XXX: is there a better way?
-        ec = space.getexecutioncontext()
-        Action.perform_actions(space.pending_actions)
-        Action.perform_actions(ec.pending_actions)
     try:
         msg = os.strerror(errno)
     except ValueError:



More information about the Pypy-commit mailing list