Commits

Armin Rigo committed 131909e

Preparing for the merge of default

  • Participants
  • Parent commits 009b406
  • Branches stm-thread-2

Comments (0)

Files changed (4)

File pypy/module/signal/__init__.py

     }
 
     def buildloaders(cls):
-        from pypy.rlib import rsignal
+        from rpython.rlib import rsignal
+
         for name in rsignal.signal_names:
             signum = getattr(rsignal, name)
             if signum is not None:

File pypy/module/signal/interp_signal.py

 from __future__ import with_statement
 
 import signal as cpy_signal
+import sys
 
 from pypy.interpreter.error import OperationError, exception_from_errno
 from pypy.interpreter.executioncontext import (AsyncAction, AbstractActionFlag,
     PeriodicAsyncAction)
 from pypy.interpreter.gateway import unwrap_spec
 
-from pypy.rlib import jit, rposix
-from pypy.rlib.rarithmetic import intmask
-from pypy.rlib.rsignal import (pypysig_getaddr_occurred, pypysig_setflag,
-    pypysig_poll, pypysig_reinstall, pypysig_ignore, pypysig_default,
-    pypysig_set_wakeup_fd, c_alarm, c_pause, c_getitimer, c_setitimer,
-    c_siginterrupt, itimervalP, NSIG, SIG_DFL, SIG_IGN, ITIMER_REAL,
-    ITIMER_PROF, ITIMER_VIRTUAL, signal_values)
-from pypy.rpython.lltypesystem import lltype, rffi
+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
+
+
+WIN32 = sys.platform == 'win32'
 
 
 class SignalActionFlag(AbstractActionFlag):
         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
 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.pending_signal = n
+                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:
     None -- if an unknown handler is in effect (XXX UNIMPLEMENTED)
     anything else -- the callable Python object used as a handler
     """
-    check_signum_in_range(space, signum)
+    if WIN32:
+        check_signum_exists(space, signum)
+    else:
+        check_signum_in_range(space, signum)
     action = space.check_signal_action
     if signum in action.handlers_w:
         return action.handlers_w[signum]
     return space.wrap(SIG_DFL)
 
+
 def default_int_handler(space, w_signum, w_frame):
     """
     default_int_handler(...)
     raise OperationError(space.w_KeyboardInterrupt,
                          space.w_None)
 
+
 @jit.dont_look_inside
 @unwrap_spec(timeout=int)
 def alarm(space, timeout):
     return space.wrap(c_alarm(timeout))
 
+
 @jit.dont_look_inside
 def pause(space):
     c_pause()
     return space.w_None
 
+
 def check_signum_exists(space, signum):
     if signum in signal_values:
         return
     raise OperationError(space.w_ValueError,
                          space.wrap("invalid signal value"))
 
+
 def check_signum_in_range(space, signum):
     if 1 <= signum < NSIG:
         return
     A signal handler function is called with two arguments:
     the first is the signal number, the second is the interrupted stack frame.
     """
-    ec      = space.getexecutioncontext()
+    ec = space.getexecutioncontext()
     main_ec = space.threadlocals.getmainthreadvalue()
 
     old_handler = getsignal(space, signum)
     action.handlers_w[signum] = w_handler
     return old_handler
 
+
 @jit.dont_look_inside
 @unwrap_spec(fd=int)
 def set_wakeup_fd(space, fd):
     """Sets the fd to be written to (with '\0') when a signal
     comes in.  Returns the old fd.  A library can use this to
     wakeup select or poll.  The previous fd is returned.
-    
+
     The fd must be non-blocking.
     """
     if space.config.objspace.usemodules.thread:
     old_fd = pypysig_set_wakeup_fd(fd)
     return space.wrap(intmask(old_fd))
 
+
 @jit.dont_look_inside
 @unwrap_spec(signum=int, flag=int)
 def siginterrupt(space, signum, flag):
     rffi.setintfield(timeval, 'c_tv_sec', int(d))
     rffi.setintfield(timeval, 'c_tv_usec', int((d - int(d)) * 1000000))
 
+
 def double_from_timeval(tv):
     return rffi.getintfield(tv, 'c_tv_sec') + (
         rffi.getintfield(tv, 'c_tv_usec') / 1000000.0)
 
+
 def itimer_retval(space, val):
     w_value = space.wrap(double_from_timeval(val.c_it_value))
     w_interval = space.wrap(double_from_timeval(val.c_it_interval))
     return space.newtuple([w_value, w_interval])
 
+
 class Cache:
     def __init__(self, space):
         self.w_itimererror = space.new_exception_class("signal.ItimerError",
                                                        space.w_IOError)
 
+
 def get_itimer_error(space):
     return space.fromcache(Cache).w_itimererror
 
+
 @jit.dont_look_inside
 @unwrap_spec(which=int, first=float, interval=float)
 def setitimer(space, which, first, interval=0):
     """setitimer(which, seconds[, interval])
-    
     Sets given itimer (one of ITIMER_REAL, ITIMER_VIRTUAL
+
     or ITIMER_PROF) to fire after value seconds and after
     that every interval seconds.
     The itimer can be cleared by setting seconds to zero.
-    
+
     Returns old values as a tuple: (delay, interval).
     """
     with lltype.scoped_alloc(itimervalP.TO, 1) as new:
             if ret != 0:
                 raise exception_from_errno(space, get_itimer_error(space))
 
+            return itimer_retval(space, old[0])
 
-            return itimer_retval(space, old[0])
 
 @jit.dont_look_inside
 @unwrap_spec(which=int)
 def getitimer(space, which):
     """getitimer(which)
-    
+
     Returns current value of given itimer.
     """
     with lltype.scoped_alloc(itimervalP.TO, 1) as old:

File pypy/module/signal/test/test_interp_signal.py

-import os, py
-from pypy.translator.c.test.test_genc import compile
-from pypy.module.signal import interp_signal
-
-def setup_module(mod):
-    if not hasattr(os, 'kill') or not hasattr(os, 'getpid'):
-        py.test.skip("requires os.kill() and os.getpid()")
-    if not hasattr(interp_signal, 'SIGUSR1'):
-        py.test.skip("requires SIGUSR1 in signal")
-
-
-def check(expected):
-    res = interp_signal.pypysig_poll()
-    os.write(1, "poll() => %d, expected %d\n" % (res, expected))
-    assert res == expected
-
-def test_simple():
-    import os
-    check(-1)
-    check(-1)
-    for i in range(3):
-        interp_signal.pypysig_setflag(interp_signal.SIGUSR1)
-        os.kill(os.getpid(), interp_signal.SIGUSR1)
-        check(interp_signal.SIGUSR1)
-        check(-1)
-        check(-1)
-
-    interp_signal.pypysig_ignore(interp_signal.SIGUSR1)
-    os.kill(os.getpid(), interp_signal.SIGUSR1)
-    check(-1)
-    check(-1)
-
-    interp_signal.pypysig_default(interp_signal.SIGUSR1)
-    check(-1)
-
-
-def test_compile():
-    fn = compile(test_simple, [])
-    fn()

File pypy/module/signal/test/test_signal.py

 
 class AppTestSignal:
     spaceconfig = {
-        "usemodules": ['signal', 'rctime'],
+        "usemodules": ['signal', 'rctime'] + (['fcntl'] if os.name != 'nt' else []),
     }
 
     def setup_class(cls):
             skip("requires os.kill() and os.getpid()")
         signal = self.signal   # the signal module to test
         if not hasattr(signal, 'SIGUSR1'):
-            py.test.skip("requires SIGUSR1 in signal")
+            skip("requires SIGUSR1 in signal")
         signum = signal.SIGUSR1
 
         received = []
         import sys
         if sys.platform == 'win32':
             raises(ValueError, signal, 42, lambda *args: None)
+            raises(ValueError, signal, 7, lambda *args: None)
+        elif sys.platform == 'darwin':
+            raises(ValueError, signal, 42, lambda *args: None)
         else:
             signal(42, lambda *args: None)
             signal(42, SIG_DFL)