[pypy-svn] r34068 - in pypy/dist/pypy: interpreter interpreter/test module/thread

arigo at codespeak.net arigo at codespeak.net
Thu Nov 2 17:49:06 CET 2006


Author: arigo
Date: Thu Nov  2 17:49:02 2006
New Revision: 34068

Added:
   pypy/dist/pypy/interpreter/test/test_executioncontext.py   (contents, props changed)
Modified:
   pypy/dist/pypy/interpreter/baseobjspace.py
   pypy/dist/pypy/interpreter/executioncontext.py
   pypy/dist/pypy/interpreter/miscutils.py
   pypy/dist/pypy/module/thread/__init__.py
   pypy/dist/pypy/module/thread/gil.py
   pypy/dist/pypy/module/thread/threadlocals.py
Log:
Add generic support for global or per-thread "actions" in the
interpreter, executed every sys.setcheckinterval() bytecode
instructions.

Adapted the GIL-releasing code to use such an "action".

Should be usable for things like soft preemptive threading (for the
logic object space), and maybe for signal handlers (e.g. Ctrl-C =>
KeyboardInterrupt).



Modified: pypy/dist/pypy/interpreter/baseobjspace.py
==============================================================================
--- pypy/dist/pypy/interpreter/baseobjspace.py	(original)
+++ pypy/dist/pypy/interpreter/baseobjspace.py	Thu Nov  2 17:49:02 2006
@@ -148,6 +148,7 @@
             config = Config(pypy_optiondescription)
         self.config = config
         self.interned_strings = {}
+        self.pending_actions = []
         self.setoptions(**kw)
 
         if self.config.objspace.logbytecodes:            

Modified: pypy/dist/pypy/interpreter/executioncontext.py
==============================================================================
--- pypy/dist/pypy/interpreter/executioncontext.py	(original)
+++ pypy/dist/pypy/interpreter/executioncontext.py	Thu Nov  2 17:49:02 2006
@@ -1,5 +1,5 @@
 import sys
-from pypy.interpreter.miscutils import Stack
+from pypy.interpreter.miscutils import Stack, Action
 from pypy.interpreter.error import OperationError
 
 def new_framestack():
@@ -16,6 +16,7 @@
         self.w_profilefunc = None
         self.is_tracing = 0
         self.ticker = 0
+        self.pending_actions = []
         self.compiler = space.createcompiler()
 
     def enter(self, frame):
@@ -106,7 +107,8 @@
         #     as selected by sys.setcheckinterval()
         ticker = self.ticker
         if ticker <= 0:
-            self.space.threadlocals.yield_thread()
+            Action.perform_actions(self.pending_actions)
+            Action.perform_actions(self.space.pending_actions)
             ticker = self.space.sys.checkinterval
         self.ticker = ticker - 1
         if frame.w_f_trace is None or self.is_tracing:
@@ -254,3 +256,6 @@
                 frame.last_exception = last_exception
                 self.is_tracing -= 1
 
+    def add_pending_action(self, action):
+        self.pending_actions.append(action)
+        self.ticker = 0

Modified: pypy/dist/pypy/interpreter/miscutils.py
==============================================================================
--- pypy/dist/pypy/interpreter/miscutils.py	(original)
+++ pypy/dist/pypy/interpreter/miscutils.py	Thu Nov  2 17:49:02 2006
@@ -165,9 +165,28 @@
     def setvalue(self, value):
         self._value = value
 
-    def yield_thread(self):
-        """Called from time to time between the interpretation of bytecodes.
-        Hook for threading models that require it."""
-
     def getGIL(self):
         return None    # XXX temporary hack!
+
+
+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)

Added: pypy/dist/pypy/interpreter/test/test_executioncontext.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/interpreter/test/test_executioncontext.py	Thu Nov  2 17:49:02 2006
@@ -0,0 +1,36 @@
+import py
+from pypy.interpreter import miscutils
+
+
+class TestExecutionContext:
+
+    def test_action(self):
+        class Finished(Exception):
+            pass
+
+        class DemoAction(miscutils.Action):
+            def __init__(self, repeat):
+                self.repeat = repeat
+                self.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
+        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

Modified: pypy/dist/pypy/module/thread/__init__.py
==============================================================================
--- pypy/dist/pypy/module/thread/__init__.py	(original)
+++ pypy/dist/pypy/module/thread/__init__.py	Thu Nov  2 17:49:02 2006
@@ -27,6 +27,8 @@
         space.threadlocals = gil.GILThreadLocals()
         space.threadlocals.setvalue(prev)
         space.threadlocals.enter_thread(space)   # setup the main thread
+        # add the GIL-releasing callback as an action on the space
+        space.pending_actions.append(gil.GILReleaseAction(space.threadlocals))
 
     def setup_after_space_initialization(self):
         # the import lock is in imp.py.  Import it after the space is fully

Modified: pypy/dist/pypy/module/thread/gil.py
==============================================================================
--- pypy/dist/pypy/module/thread/gil.py	(original)
+++ pypy/dist/pypy/module/thread/gil.py	Thu Nov  2 17:49:02 2006
@@ -8,6 +8,7 @@
 # from time to time, using the executioncontext's XXX
 
 import thread
+from pypy.interpreter.miscutils import Action
 from pypy.module.thread.threadlocals import OSThreadLocals
 
 
@@ -39,3 +40,16 @@
 
     def getGIL(self):
         return self.GIL    # XXX temporary hack!
+
+
+class GILReleaseAction(Action):
+    """An action called when the current thread is between two bytecodes
+    (so that it's a good time to yield some time to other threads).
+    """
+    repeat = True
+
+    def __init__(self, threadlocals):
+        self.threadlocals = threadlocals
+
+    def perform(self):
+        self.threadlocals.yield_thread()

Modified: pypy/dist/pypy/module/thread/threadlocals.py
==============================================================================
--- pypy/dist/pypy/module/thread/threadlocals.py	(original)
+++ pypy/dist/pypy/module/thread/threadlocals.py	Thu Nov  2 17:49:02 2006
@@ -40,10 +40,6 @@
             except KeyError:
                 pass
 
-    def yield_thread(self):
-        """Notification that the current thread is between two bytecodes
-        (so that it's a good time to yield some time to other threads)."""
-
     def atthreadexit(self, space, exit_func, w_obj):
         ec = space.getexecutioncontext()
         ec.thread_exit_funcs.append((exit_func, w_obj))



More information about the Pypy-commit mailing list