[pypy-svn] r56127 - in pypy/branch/async-del/pypy: interpreter module/signal module/thread translator/c/src

arigo at codespeak.net arigo at codespeak.net
Fri Jun 27 18:33:13 CEST 2008


Author: arigo
Date: Fri Jun 27 18:33:10 2008
New Revision: 56127

Modified:
   pypy/branch/async-del/pypy/interpreter/executioncontext.py
   pypy/branch/async-del/pypy/module/signal/__init__.py
   pypy/branch/async-del/pypy/module/signal/interp_signal.py
   pypy/branch/async-del/pypy/module/thread/gil.py
   pypy/branch/async-del/pypy/translator/c/src/signals.h
Log:
Not completely nice, but this should make signals and threads
work again.


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	Fri Jun 27 18:33:10 2008
@@ -343,7 +343,7 @@
     BYTECODE_COUNTER_OVERFLOW_BIT = (1 << 20)
 
     # Free bits
-    FREE_BITS = [1 << _b for _b in range(21, LONG_BIT)]
+    FREE_BITS = [1 << _b for _b in range(21, LONG_BIT-1)]
 
     # The acceptable range of values for sys.checkinterval, so that
     # the bytecode_counter fits in 20 bits
@@ -366,6 +366,14 @@
         The action must have been registered at space initalization time."""
         self.space.actionflag.fire(self)
 
+    def fire_after_thread_switch(self):
+        """Bit of a hack: fire() the action but only the next time the GIL
+        is released and re-acquired (i.e. after a portential thread switch).
+        Don't call this if threads are not enabled.
+        """
+        from pypy.module.thread.gil import spacestate
+        spacestate.set_actionflag_bit_after_thread_switch |= self.bitmask
+
     def perform(self, executioncontext):
         """To be overridden."""
 

Modified: pypy/branch/async-del/pypy/module/signal/__init__.py
==============================================================================
--- pypy/branch/async-del/pypy/module/signal/__init__.py	(original)
+++ pypy/branch/async-del/pypy/module/signal/__init__.py	Fri Jun 27 18:33:10 2008
@@ -28,8 +28,10 @@
         from pypy.module.signal import interp_signal
         MixedModule.__init__(self, space, *args)
         # add the signal-checking callback as an action on the space
-        space.pending_actions.append(interp_signal.CheckSignalAction(space))
-        # use the C-level pypysig_occurred variable as the tick counter
+        space.check_signal_action = interp_signal.CheckSignalAction(space)
+        space.actionflag.register_action(space.check_signal_action)
+        # use the C-level pypysig_occurred variable as the action flag
+        # (the result is that the C-level signal handler will directly
+        # set the flag for the CheckSignalAction)
         space.actionflag.get = interp_signal.pypysig_get_occurred
         space.actionflag.set = interp_signal.pypysig_set_occurred
-

Modified: pypy/branch/async-del/pypy/module/signal/interp_signal.py
==============================================================================
--- pypy/branch/async-del/pypy/module/signal/interp_signal.py	(original)
+++ pypy/branch/async-del/pypy/module/signal/interp_signal.py	Fri Jun 27 18:33:10 2008
@@ -1,6 +1,7 @@
 from pypy.interpreter.error import OperationError
 from pypy.interpreter.baseobjspace import W_Root, ObjSpace
-from pypy.interpreter.miscutils import Action
+from pypy.interpreter.executioncontext import AsyncAction
+from pypy.rlib.rarithmetic import LONG_BIT, intmask
 import signal as cpy_signal
 from pypy.rpython.lltypesystem import lltype, rffi
 from pypy.translator.tool.cbuild import ExternalCompilationInfo
@@ -40,55 +41,89 @@
 pypysig_set_occurred = external('pypysig_set_occurred', [lltype.Signed],
                                 lltype.Void, _nowrapper=True)
 
-class CheckSignalAction(Action):
-    """A repeatitive action at the space level, checking if the
-    signal_occurred flag is set and if so, scheduling ReportSignal actions.
-    """
-    repeat = True
+class CheckSignalAction(AsyncAction):
+    """An action that is automatically invoked when a signal is received."""
+
+    # The C-level signal handler sets the highest bit of pypysig_occurred:
+    bitmask = intmask(1 << (LONG_BIT-1))
 
     def __init__(self, space):
-        self.space = space
+        AsyncAction.__init__(self, space)
         self.handlers_w = {}
+        if space.config.objspace.usemodules.thread:
+            # need a helper action in case signals arrive in a non-main thread
+            self.pending_signals = {}
+            self.reissue_signal_action = ReissueSignalAction(space)
+            space.actionflag.register_action(self.reissue_signal_action)
+        else:
+            self.reissue_signal_action = None
 
-    def perform(self):
+    def perform(self, executioncontext):
         while True:
             n = pypysig_poll()
             if n < 0:
                 break
-            main_ec = self.space.threadlocals.getmainthreadvalue()
-            main_ec.add_pending_action(ReportSignal(self, n))
+            if self.reissue_signal_action is None:
+                # no threads: we can report the signal immediately
+                self.report_signal(n)
+            else:
+                main_ec = self.space.threadlocals.getmainthreadvalue()
+                if executioncontext is main_ec:
+                    # running in the main thread: we can report the
+                    # signal immediately
+                    self.report_signal(n)
+                else:
+                    # running in another thread: we need to hack a bit
+                    self.pending_signals[n] = None
+                    self.reissue_signal_action.fire_after_thread_switch()
 
-    def get(space):
-        for action in space.pending_actions:
-            if isinstance(action, CheckSignalAction):
-                return action
-        raise OperationError(space.w_RuntimeError,
-                             space.wrap("lost CheckSignalAction"))
-    get = staticmethod(get)
-
-
-class ReportSignal(Action):
-    """A one-shot action for the main thread's execution context."""
-
-    def __init__(self, action, signum):
-        self.action = action
-        self.signum = signum
-
-    def perform(self):
+    def report_signal(self, n):
         try:
-            w_handler = self.action.handlers_w[self.signum]
+            w_handler = self.handlers_w[n]
         except KeyError:
             return    # no handler, ignore signal
         # re-install signal handler, for OSes that clear it
-        pypysig_setflag(self.signum)
+        pypysig_setflag(n)
         # invoke the app-level handler
-        space = self.action.space
+        space = self.space
         ec = space.getexecutioncontext()
         try:
             w_frame = ec.framestack.top()
         except IndexError:
             w_frame = space.w_None
-        space.call_function(w_handler, space.wrap(self.signum), w_frame)
+        space.call_function(w_handler, space.wrap(n), w_frame)
+
+    def report_pending_signals(self):
+        # XXX this logic isn't so complicated but I have no clue how
+        # to test it :-(
+        pending_signals = self.pending_signals.keys()
+        self.pending_signals.clear()
+        try:
+            while pending_signals:
+                self.report_signal(pending_signals.pop())
+        finally:
+            # in case of exception, put the undelivered signals back
+            # into the dict instead of silently swallowing them
+            if pending_signals:
+                for n in pending_signals:
+                    self.pending_signals[n] = None
+                self.reissue_signal_action.fire()
+
+
+class ReissueSignalAction(AsyncAction):
+    """A special action to help deliver signals to the main thread.  If
+    a non-main thread caught a signal, this action fires after every
+    thread switch until we land in the main thread.
+    """
+
+    def perform(self):
+        main_ec = self.space.threadlocals.getmainthreadvalue()
+        if executioncontext is main_ec:
+            # now running in the main thread: we can really report the signals
+            self.space.check_signal_action.report_pending_signals()
+        else:
+            # still running in some other thread: try again later
+            self.fire_after_thread_switch()
 
 
 def getsignal(space, signum):
@@ -101,7 +136,7 @@
     None -- if an unknown handler is in effect (XXX UNIMPLEMENTED)
     anything else -- the callable Python object used as a handler
     """
-    action = CheckSignalAction.get(space)
+    action = space.check_signal_action
     if signum in action.handlers_w:
         return action.handlers_w[signum]
     return space.wrap(SIG_DFL)
@@ -129,7 +164,7 @@
         raise OperationError(space.w_ValueError,
                              space.wrap("signal() must be called from the "
                                         "main thread"))
-    action = CheckSignalAction.get(space)
+    action = space.check_signal_action
     if space.eq_w(w_handler, space.wrap(SIG_DFL)):
         pypysig_default(signum)
         action.handlers_w[signum] = w_handler

Modified: pypy/branch/async-del/pypy/module/thread/gil.py
==============================================================================
--- pypy/branch/async-del/pypy/module/thread/gil.py	(original)
+++ pypy/branch/async-del/pypy/module/thread/gil.py	Fri Jun 27 18:33:10 2008
@@ -42,6 +42,7 @@
         # test_compile_lock.  As a workaround, we repatch these global
         # fields systematically.
         spacestate.ll_GIL = self.ll_GIL
+        spacestate.actionflag = space.actionflag
         invoke_around_extcall(before_external_call, after_external_call)
         return result
 
@@ -68,9 +69,22 @@
 
 
 class SpaceState:
+
     def _freeze_(self):
         self.ll_GIL = thread.null_ll_lock
+        self.actionflag = None
+        self.set_actionflag_bit_after_thread_switch = 0
         return False
+
+    def after_thread_switch(self):
+        # this is support logic for the signal module, to help it deliver
+        # signals to the main thread.
+        actionflag = self.actionflag
+        if actionflag is not None:
+            flag = actionflag.get()
+            flag |= self.set_actionflag_bit_after_thread_switch
+            actionflag.set(flag)
+
 spacestate = SpaceState()
 
 # Fragile code below.  We have to preserve the C-level errno manually...
@@ -87,5 +101,6 @@
     e = get_errno()
     thread.acquire_NOAUTO(spacestate.ll_GIL, True)
     thread.gc_thread_run()
+    spacestate.after_thread_switch()
     set_errno(e)
 after_external_call._gctransformer_hint_cannot_collect_ = True

Modified: pypy/branch/async-del/pypy/translator/c/src/signals.h
==============================================================================
--- pypy/branch/async-del/pypy/translator/c/src/signals.h	(original)
+++ pypy/branch/async-del/pypy/translator/c/src/signals.h	Fri Jun 27 18:33:10 2008
@@ -4,6 +4,8 @@
 #ifndef _PYPY_SIGNALS_H
 #define _PYPY_SIGNALS_H
 
+#include "Python.h"   /* XXX  for LONG_MIN */
+
 #include <stdlib.h>
 
 #ifdef MS_WINDOWS
@@ -56,9 +58,19 @@
 #define PENDING_SIGNAL_BIT   (LONG_MIN)   /* high bit */
 extern long pypysig_occurred;
 
-/* inlinable helpers to get/set the variable as efficiently as possible */
-static long pypysig_get_occurred(void) { return pypysig_occurred; }
-static void pypysig_set_occurred(long x) { pypysig_occurred = x; }
+/* some C tricks to get/set the variable as efficiently as possible:
+   use macros when compiling as a stand-alone program, but still
+   export a function with the correct name for testing */
+#undef pypysig_get_occurred
+#undef pypysig_set_occurred
+long pypysig_get_occurred(void);
+void pypysig_set_occurred(long x);
+#ifndef PYPY_NOT_MAIN_FILE
+long pypysig_get_occurred(void) { return pypysig_occurred; }
+void pypysig_set_occurred(long x) { pypysig_occurred = x; }
+#endif
+#define pypysig_get_occurred()   (pypysig_occurred)
+#define pypysig_set_occurred(x)  (pypysig_occurred=(x))
 
 /************************************************************/
 /* Implementation                                           */
@@ -123,10 +135,15 @@
 
 int pypysig_poll(void)
 {
-  if (pypysig_occurred & PENDING_SIGNAL_BIT)
+  /* the two commented out lines below are useful for performance in
+     normal usage of pypysig_poll(); however, pypy/module/signal/ is
+     not normal usage.  It only calls pypysig_poll() if the
+     PENDING_SIGNAL_BIT is set, and it clears that bit first. */
+
+/* if (pypysig_occurred & PENDING_SIGNAL_BIT) */
     {
       int i;
-      pypysig_occurred &= ~PENDING_SIGNAL_BIT;
+/*     pypysig_occurred &= ~PENDING_SIGNAL_BIT; */
       for (i=0; i<NSIG; i++)
         if (pypysig_flags[i])
           {



More information about the Pypy-commit mailing list