Commit b654a6c0 authored by Jean-Paul Calderone's avatar Jean-Paul Calderone

Merged revisions 81007 via svnmerge from

svn+ssh://pythondev@svn.python.org/python/trunk

........
  r81007 | jean-paul.calderone | 2010-05-08 16:06:02 -0400 (Sat, 08 May 2010) | 1 line

  Skip signal handler re-installation if it is not necessary.  Issue 8354.
........
parent a555e2e6
...@@ -256,48 +256,121 @@ class WakeupSignalTests(unittest.TestCase): ...@@ -256,48 +256,121 @@ class WakeupSignalTests(unittest.TestCase):
class SiginterruptTest(unittest.TestCase): class SiginterruptTest(unittest.TestCase):
signum = signal.SIGUSR1 signum = signal.SIGUSR1
def readpipe_interrupted(self, cb):
def setUp(self):
"""Install a no-op signal handler that can be set to allow
interrupts or not, and arrange for the original signal handler to be
re-installed when the test is finished.
"""
self._cleanups = []
oldhandler = signal.signal(self.signum, lambda x,y: None)
self.addCleanup(signal.signal, self.signum, oldhandler)
def tearDown(self):
"""Run any cleanup functions which have been registered.
"""
for (f, a) in self._cleanups:
f(*a)
def addCleanup(self, f, *a):
"""Register a function to be called at the end of the test method
run.
"""
self._cleanups.append((f, a))
def readpipe_interrupted(self):
"""Perform a read during which a signal will arrive. Return True if the
read is interrupted by the signal and raises an exception. Return False
if it returns normally.
"""
# Create a pipe that can be used for the read. Also clean it up
# when the test is over, since nothing else will (but see below for
# the write end).
r, w = os.pipe() r, w = os.pipe()
self.addCleanup(os.close, r)
# Create another process which can send a signal to this one to try
# to interrupt the read.
ppid = os.getpid() ppid = os.getpid()
pid = os.fork() pid = os.fork()
oldhandler = signal.signal(self.signum, lambda x,y: None) if pid == 0:
cb() # Child code: sleep to give the parent enough time to enter the
if pid==0: # read() call (there's a race here, but it's really tricky to
# child code: sleep, kill, sleep. and then exit, # eliminate it); then signal the parent process. Also, sleep
# which closes the pipe from which the parent process reads # again to make it likely that the signal is delivered to the
# parent process before the child exits. If the child exits
# first, the write end of the pipe will be closed and the test
# is invalid.
try: try:
time.sleep(0.2) time.sleep(0.2)
os.kill(ppid, self.signum) os.kill(ppid, self.signum)
time.sleep(0.2) time.sleep(0.2)
finally: finally:
# No matter what, just exit as fast as possible now.
exit_subprocess() exit_subprocess()
else:
try: # Parent code.
# Make sure the child is eventually reaped, else it'll be a
# zombie for the rest of the test suite run.
self.addCleanup(os.waitpid, pid, 0)
# Close the write end of the pipe. The child has a copy, so
# it's not really closed until the child exits. We need it to
# close when the child exits so that in the non-interrupt case
# the read eventually completes, otherwise we could just close
# it *after* the test.
os.close(w) os.close(w)
# Try the read and report whether it is interrupted or not to
# the caller.
try: try:
d=os.read(r, 1) d = os.read(r, 1)
return False return False
except OSError, err: except OSError, err:
if err.errno != errno.EINTR: if err.errno != errno.EINTR:
raise raise
return True return True
finally:
signal.signal(self.signum, oldhandler)
os.waitpid(pid, 0)
def test_without_siginterrupt(self): def test_without_siginterrupt(self):
i=self.readpipe_interrupted(lambda: None) """If a signal handler is installed and siginterrupt is not called
self.assertEquals(i, True) at all, when that signal arrives, it interrupts a syscall that's in
progress.
"""
i = self.readpipe_interrupted()
self.assertTrue(i)
# Arrival of the signal shouldn't have changed anything.
i = self.readpipe_interrupted()
self.assertTrue(i)
def test_siginterrupt_on(self): def test_siginterrupt_on(self):
i=self.readpipe_interrupted(lambda: signal.siginterrupt(self.signum, 1)) """If a signal handler is installed and siginterrupt is called with
self.assertEquals(i, True) a true value for the second argument, when that signal arrives, it
interrupts a syscall that's in progress.
"""
signal.siginterrupt(self.signum, 1)
i = self.readpipe_interrupted()
self.assertTrue(i)
# Arrival of the signal shouldn't have changed anything.
i = self.readpipe_interrupted()
self.assertTrue(i)
def test_siginterrupt_off(self): def test_siginterrupt_off(self):
i=self.readpipe_interrupted(lambda: signal.siginterrupt(self.signum, 0)) """If a signal handler is installed and siginterrupt is called with
self.assertEquals(i, False) a false value for the second argument, when that signal arrives, it
does not interrupt a syscall that's in progress.
"""
signal.siginterrupt(self.signum, 0)
i = self.readpipe_interrupted()
self.assertFalse(i)
# Arrival of the signal shouldn't have changed anything.
i = self.readpipe_interrupted()
self.assertFalse(i)
class ItimerTest(unittest.TestCase): class ItimerTest(unittest.TestCase):
def setUp(self): def setUp(self):
......
...@@ -73,6 +73,9 @@ Library ...@@ -73,6 +73,9 @@ Library
- Issue #4687: Fix accuracy of garbage collection runtimes displayed with - Issue #4687: Fix accuracy of garbage collection runtimes displayed with
gc.DEBUG_STATS. gc.DEBUG_STATS.
- Issue #8354: The siginterrupt setting is now preserved for all signals,
not just SIGCHLD.
- Issue #8577: distutils.sysconfig.get_python_inc() now makes a difference - Issue #8577: distutils.sysconfig.get_python_inc() now makes a difference
between the build dir and the source dir when looking for "python.h" or between the build dir and the source dir when looking for "python.h" or
"Include". "Include".
......
...@@ -192,7 +192,12 @@ signal_handler(int sig_num) ...@@ -192,7 +192,12 @@ signal_handler(int sig_num)
return; return;
} }
#endif #endif
#ifndef HAVE_SIGACTION
/* If the handler was not set up with sigaction, reinstall it. See
* Python/pythonrun.c for the implementation of PyOS_setsig which
* makes this true. See also issue8354. */
PyOS_setsig(sig_num, signal_handler); PyOS_setsig(sig_num, signal_handler);
#endif
} }
......
...@@ -1867,6 +1867,10 @@ PyOS_sighandler_t ...@@ -1867,6 +1867,10 @@ PyOS_sighandler_t
PyOS_setsig(int sig, PyOS_sighandler_t handler) PyOS_setsig(int sig, PyOS_sighandler_t handler)
{ {
#ifdef HAVE_SIGACTION #ifdef HAVE_SIGACTION
/* Some code in Modules/signalmodule.c depends on sigaction() being
* used here if HAVE_SIGACTION is defined. Fix that if this code
* changes to invalidate that assumption.
*/
struct sigaction context, ocontext; struct sigaction context, ocontext;
context.sa_handler = handler; context.sa_handler = handler;
sigemptyset(&context.sa_mask); sigemptyset(&context.sa_mask);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment