Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r60607:05672556b297
Date: 2013-01-28 14:28 +0100
http://bitbucket.org/pypy/pypy/changeset/05672556b297/

Log:    Replace the fire_after_thread_switch() logic with different logic,
        which is just as subtle as the original one but maybe more correct
        :-/

diff --git a/pypy/interpreter/executioncontext.py 
b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -343,9 +343,13 @@
         signal, the tick counter is set to -1 by C code in signals.h.
         """
         assert isinstance(action, PeriodicAsyncAction)
-        self._periodic_actions.append(action)
+        # hack to put the release-the-GIL one at the end of the list,
+        # and the report-the-signals one at the start of the list.
         if use_bytecode_counter:
+            self._periodic_actions.append(action)
             self.has_bytecode_counter = True
+        else:
+            self._periodic_actions.insert(0, action)
         self._rebuild_action_dispatcher()
 
     def getcheckinterval(self):
@@ -419,15 +423,6 @@
         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 potential thread switch).
-        Don't call this if threads are not enabled.  Currently limited to
-        one action (i.e. reserved for CheckSignalAction from module/signal).
-        """
-        from pypy.module.thread.gil import spacestate
-        spacestate.action_after_thread_switch = self
-
     def perform(self, executioncontext, frame):
         """To be overridden."""
 
diff --git a/pypy/module/signal/interp_signal.py 
b/pypy/module/signal/interp_signal.py
--- a/pypy/module/signal/interp_signal.py
+++ b/pypy/module/signal/interp_signal.py
@@ -8,7 +8,8 @@
     PeriodicAsyncAction)
 from pypy.interpreter.gateway import unwrap_spec
 
-from rpython.rlib import jit, rposix
+from rpython.rlib import jit, rposix, rgc
+from rpython.rlib.objectmodel import we_are_translated
 from rpython.rlib.rarithmetic import intmask
 from rpython.rlib.rsignal import *
 from rpython.rtyper.lltypesystem import lltype, rffi
@@ -31,6 +32,11 @@
         p = pypysig_getaddr_occurred()
         p.c_value = value
 
+    @staticmethod
+    def rearm_ticker():
+        p = pypysig_getaddr_occurred()
+        p.c_value = -1
+
     def decrement_ticker(self, by):
         p = pypysig_getaddr_occurred()
         value = p.c_value
@@ -46,41 +52,62 @@
 class CheckSignalAction(PeriodicAsyncAction):
     """An action that is automatically invoked when a signal is received."""
 
+    # Note that this is a PeriodicAsyncAction: it means more precisely
+    # that it is called whenever the C-level ticker becomes < 0.
+    # Without threads, it is only ever set to -1 when we receive a
+    # signal.  With threads, it also decrements steadily (but slowly).
+
     def __init__(self, space):
+        "NOT_RPYTHON"
         AsyncAction.__init__(self, space)
         self.handlers_w = {}
-        self.emulated_sigint = False
+        self.pending_signal = -1
+        self.fire_in_main_thread = False
+        if self.space.config.objspace.usemodules.thread:
+            from pypy.module.thread import gil
+            gil.after_thread_switch = self._after_thread_switch
+
+    @rgc.no_collect
+    def _after_thread_switch(self):
+        if self.fire_in_main_thread:
+            if self.space.threadlocals.ismainthread():
+                self.fire_in_main_thread = False
+                SignalActionFlag.rearm_ticker()
+                # this occurs when we just switched to the main thread
+                # and there is a signal pending: we force the ticker to
+                # -1, which should ensure perform() is called quickly.
 
     @jit.dont_look_inside
     def perform(self, executioncontext, frame):
-        if self.space.config.objspace.usemodules.thread:
-            main_ec = self.space.threadlocals.getmainthreadvalue()
-            in_main = executioncontext is main_ec
-        else:
-            in_main = True
-        # If we are in the main thread, poll and report the signals now.
-        if in_main:
-            if self.emulated_sigint:
-                self.emulated_sigint = False
-                self._report_signal(cpy_signal.SIGINT)
-            while True:
-                n = pypysig_poll()
-                if n < 0:
-                    break
+        # Poll for the next signal, if any
+        n = self.pending_signal
+        if n < 0: n = pypysig_poll()
+        while n >= 0:
+            if self.space.config.objspace.usemodules.thread:
+                in_main = self.space.threadlocals.ismainthread()
+            else:
+                in_main = True
+            if in_main:
+                # If we are in the main thread, report the signal now,
+                # and poll more
+                self.pending_signal = -1
                 self._report_signal(n)
-        else:
-            # Otherwise, don't call pypysig_poll() at all.  Instead,
-            # arrange for perform() to be called again after a thread
-            # switch.  It might be called again and again, until we
-            # land in the main thread.
-            self.fire_after_thread_switch()
+                n = self.pending_signal
+                if n < 0: n = pypysig_poll()
+            else:
+                # Otherwise, arrange for perform() to be called again
+                # after we switch to the main thread.
+                self.fire_in_main_thread = True
+                break
 
-    @jit.dont_look_inside
     def set_interrupt(self):
         "Simulates the effect of a SIGINT signal arriving"
-        ec = self.space.getexecutioncontext()
-        self.emulated_sigint = True
-        self.perform(ec, None)
+        if not we_are_translated():
+            self.pending_signal = cpy_signal.SIGINT
+            # ^^^ may override another signal, but it's just for testing
+        else:
+            pypysig_pushback(cpy_signal.SIGINT)
+        self.fire_in_main_thread = True
 
     def _report_signal(self, n):
         try:
diff --git a/pypy/module/thread/gil.py b/pypy/module/thread/gil.py
--- a/pypy/module/thread/gil.py
+++ b/pypy/module/thread/gil.py
@@ -62,22 +62,7 @@
         do_yield_thread()
 
 
-class SpaceState:
-
-    def _cleanup_(self):
-        self.action_after_thread_switch = None
-        # ^^^ set by AsyncAction.fire_after_thread_switch()
-
-    def after_thread_switch(self):
-        # this is support logic for the signal module, to help it deliver
-        # signals to the main thread.
-        action = self.action_after_thread_switch
-        if action is not None:
-            self.action_after_thread_switch = None
-            action.fire()
-
-spacestate = SpaceState()
-spacestate._cleanup_()
+after_thread_switch = lambda: None     # hook for signal.py
 
 # Fragile code below.  We have to preserve the C-level errno manually...
 
@@ -94,7 +79,7 @@
     e = get_errno()
     thread.gil_acquire()
     thread.gc_thread_run()
-    spacestate.after_thread_switch()
+    after_thread_switch()
     set_errno(e)
 after_external_call._gctransformer_hint_cannot_collect_ = True
 after_external_call._dont_reach_me_in_del_ = True
@@ -112,7 +97,7 @@
     # the same thread).
     if thread.gil_yield_thread():
         thread.gc_thread_run()
-        spacestate.after_thread_switch()
+        after_thread_switch()
 do_yield_thread._gctransformer_hint_close_stack_ = True
 do_yield_thread._dont_reach_me_in_del_ = True
 do_yield_thread._dont_inline_ = True
diff --git a/pypy/module/thread/threadlocals.py 
b/pypy/module/thread/threadlocals.py
--- a/pypy/module/thread/threadlocals.py
+++ b/pypy/module/thread/threadlocals.py
@@ -48,6 +48,9 @@
         ident = self._mainthreadident
         return self._valuedict.get(ident, None)
 
+    def ismainthread(self):
+        return thread.get_ident() == self._mainthreadident
+
     def getallvalues(self):
         return self._valuedict
 
diff --git a/rpython/rlib/rsignal.py b/rpython/rlib/rsignal.py
--- a/rpython/rlib/rsignal.py
+++ b/rpython/rlib/rsignal.py
@@ -80,6 +80,8 @@
 pypysig_poll = external('pypysig_poll', [], rffi.INT, threadsafe=False)
 # don't bother releasing the GIL around a call to pypysig_poll: it's
 # pointless and a performance issue
+pypysig_pushback = external('pypysig_pushback', [rffi.INT], lltype.Void,
+                            threadsafe=False)
 
 # don't use rffi.LONGP because the JIT doesn't support raw arrays so far
 struct_name = 'pypysig_long_struct'
diff --git a/rpython/translator/c/src/signals.c 
b/rpython/translator/c/src/signals.c
--- a/rpython/translator/c/src/signals.c
+++ b/rpython/translator/c/src/signals.c
@@ -71,7 +71,7 @@
 #endif
 }
 
-static void signal_setflag_handler(int signum)
+static void pypysig_pushback(int signum)
 {
     if (0 <= signum && signum < NSIG)
       {
@@ -79,6 +79,11 @@
         pypysig_occurred = 1;
         pypysig_counter.value = -1;
       }
+}
+
+static void signal_setflag_handler(int signum)
+{
+    pypysig_pushback(signum);
 
     if (wakeup_fd != -1) 
       {
diff --git a/rpython/translator/c/src/signals.h 
b/rpython/translator/c/src/signals.h
--- a/rpython/translator/c/src/signals.h
+++ b/rpython/translator/c/src/signals.h
@@ -11,6 +11,7 @@
 
 /* utility to poll for signals that arrived */
 int pypysig_poll(void);   /* => signum or -1 */
+void pypysig_pushback(int signum);
 
 /* When a signal is received, pypysig_counter is set to -1. */
 /* This is a struct for the JIT. See rsignal.py. */
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to