summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2017-05-10 00:37:42 (GMT)
committerGitHub <noreply@github.com>2017-05-10 00:37:42 (GMT)
commit211a392cc15f9a7b1b8ce65d8f6c9f8237d1b77f (patch)
treee00d3bce60207655ec5b229bb594d2a654f8dd6c
parent291557e2904fa149ee3d56142f41acf63c47fea3 (diff)
downloadcpython-211a392cc15f9a7b1b8ce65d8f6c9f8237d1b77f.zip
cpython-211a392cc15f9a7b1b8ce65d8f6c9f8237d1b77f.tar.gz
cpython-211a392cc15f9a7b1b8ce65d8f6c9f8237d1b77f.tar.bz2
bpo-30320: test_eintr now uses pthread_sigmask() (#1523)
Rewrite sigwaitinfo() and sigtimedwait() unit tests for EINTR using pthread_sigmask() to fix a race condition between the child and the parent process. Remove the pipe which was used as a weak workaround against the race condition. sigtimedwait() is now tested with a child process sending a signal instead of testing the timeout feature which is more unstable (especially regarding to clock resolution depending on the platform).
-rw-r--r--Lib/test/eintrdata/eintr_tester.py61
1 files changed, 26 insertions, 35 deletions
diff --git a/Lib/test/eintrdata/eintr_tester.py b/Lib/test/eintrdata/eintr_tester.py
index 74a509b..1dbe88e 100644
--- a/Lib/test/eintrdata/eintr_tester.py
+++ b/Lib/test/eintrdata/eintr_tester.py
@@ -371,64 +371,55 @@ class TimeEINTRTest(EINTRBaseTest):
@unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()")
+# bpo-30320: Need pthread_sigmask() to block the signal, otherwise the test
+# is vulnerable to a race condition between the child and the parent processes.
+@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
+ 'need signal.pthread_sigmask()')
class SignalEINTRTest(EINTRBaseTest):
""" EINTR tests for the signal module. """
- @unittest.skipUnless(hasattr(signal, 'sigtimedwait'),
- 'need signal.sigtimedwait()')
- def test_sigtimedwait(self):
- t0 = time.monotonic()
- signal.sigtimedwait([signal.SIGUSR1], self.sleep_time)
- dt = time.monotonic() - t0
-
- if sys.platform.startswith('aix'):
- # On AIX, sigtimedwait(0.2) sleeps 199.8 ms
- self.assertGreaterEqual(dt, self.sleep_time * 0.9)
- else:
- self.assertGreaterEqual(dt, self.sleep_time)
-
- @unittest.skipUnless(hasattr(signal, 'sigwaitinfo'),
- 'need signal.sigwaitinfo()')
- def test_sigwaitinfo(self):
- # Issue #25277, #25868: give a few milliseconds to the parent process
- # between os.write() and signal.sigwaitinfo() to works around a race
- # condition
- self.sleep_time = 0.100
-
+ def check_sigwait(self, wait_func):
signum = signal.SIGUSR1
pid = os.getpid()
old_handler = signal.signal(signum, lambda *args: None)
self.addCleanup(signal.signal, signum, old_handler)
- rpipe, wpipe = os.pipe()
-
code = '\n'.join((
'import os, time',
'pid = %s' % os.getpid(),
'signum = %s' % int(signum),
'sleep_time = %r' % self.sleep_time,
- 'rpipe = %r' % rpipe,
- 'os.read(rpipe, 1)',
- 'os.close(rpipe)',
'time.sleep(sleep_time)',
'os.kill(pid, signum)',
))
+ old_mask = signal.pthread_sigmask(signal.SIG_BLOCK, [signum])
+ self.addCleanup(signal.pthread_sigmask, signal.SIG_UNBLOCK, [signum])
+
t0 = time.monotonic()
- proc = self.subprocess(code, pass_fds=(rpipe,))
- os.close(rpipe)
+ proc = self.subprocess(code)
with kill_on_error(proc):
- # sync child-parent
- os.write(wpipe, b'x')
- os.close(wpipe)
+ wait_func(signum)
+ dt = time.monotonic() - t0
+
+ self.assertEqual(proc.wait(), 0)
- # parent
+ @unittest.skipUnless(hasattr(signal, 'sigwaitinfo'),
+ 'need signal.sigwaitinfo()')
+ def test_sigwaitinfo(self):
+ def wait_func(signum):
signal.sigwaitinfo([signum])
- dt = time.monotonic() - t0
- self.assertEqual(proc.wait(), 0)
- self.assertGreaterEqual(dt, self.sleep_time)
+ self.check_sigwait(wait_func)
+
+ @unittest.skipUnless(hasattr(signal, 'sigtimedwait'),
+ 'need signal.sigwaitinfo()')
+ def test_sigtimedwait(self):
+ def wait_func(signum):
+ signal.sigtimedwait([signum], 120.0)
+
+ self.check_sigwait(wait_func)
@unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()")