mirror of
https://github.com/python/cpython.git
synced 2025-09-30 20:31:52 +00:00
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).
(cherry picked from commit 211a392cc1
)
This commit is contained in:
parent
418d60a525
commit
81ed537846
1 changed files with 27 additions and 31 deletions
|
@ -371,59 +371,55 @@ class TimeEINTRTest(EINTRBaseTest):
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()")
|
@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):
|
class SignalEINTRTest(EINTRBaseTest):
|
||||||
""" EINTR tests for the signal module. """
|
""" EINTR tests for the signal module. """
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(signal, 'sigtimedwait'),
|
def check_sigwait(self, wait_func):
|
||||||
'need signal.sigtimedwait()')
|
|
||||||
def test_sigtimedwait(self):
|
|
||||||
t0 = time.monotonic()
|
|
||||||
signal.sigtimedwait([signal.SIGUSR1], self.sleep_time)
|
|
||||||
dt = time.monotonic() - t0
|
|
||||||
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
|
|
||||||
|
|
||||||
signum = signal.SIGUSR1
|
signum = signal.SIGUSR1
|
||||||
pid = os.getpid()
|
pid = os.getpid()
|
||||||
|
|
||||||
old_handler = signal.signal(signum, lambda *args: None)
|
old_handler = signal.signal(signum, lambda *args: None)
|
||||||
self.addCleanup(signal.signal, signum, old_handler)
|
self.addCleanup(signal.signal, signum, old_handler)
|
||||||
|
|
||||||
rpipe, wpipe = os.pipe()
|
|
||||||
|
|
||||||
code = '\n'.join((
|
code = '\n'.join((
|
||||||
'import os, time',
|
'import os, time',
|
||||||
'pid = %s' % os.getpid(),
|
'pid = %s' % os.getpid(),
|
||||||
'signum = %s' % int(signum),
|
'signum = %s' % int(signum),
|
||||||
'sleep_time = %r' % self.sleep_time,
|
'sleep_time = %r' % self.sleep_time,
|
||||||
'rpipe = %r' % rpipe,
|
|
||||||
'os.read(rpipe, 1)',
|
|
||||||
'os.close(rpipe)',
|
|
||||||
'time.sleep(sleep_time)',
|
'time.sleep(sleep_time)',
|
||||||
'os.kill(pid, signum)',
|
'os.kill(pid, signum)',
|
||||||
))
|
))
|
||||||
|
|
||||||
t0 = time.monotonic()
|
old_mask = signal.pthread_sigmask(signal.SIG_BLOCK, [signum])
|
||||||
proc = self.subprocess(code, pass_fds=(rpipe,))
|
self.addCleanup(signal.pthread_sigmask, signal.SIG_UNBLOCK, [signum])
|
||||||
os.close(rpipe)
|
|
||||||
with kill_on_error(proc):
|
|
||||||
# sync child-parent
|
|
||||||
os.write(wpipe, b'x')
|
|
||||||
os.close(wpipe)
|
|
||||||
|
|
||||||
# parent
|
t0 = time.monotonic()
|
||||||
signal.sigwaitinfo([signum])
|
proc = self.subprocess(code)
|
||||||
|
with kill_on_error(proc):
|
||||||
|
wait_func(signum)
|
||||||
dt = time.monotonic() - t0
|
dt = time.monotonic() - t0
|
||||||
|
|
||||||
self.assertEqual(proc.wait(), 0)
|
self.assertEqual(proc.wait(), 0)
|
||||||
|
|
||||||
self.assertGreaterEqual(dt, self.sleep_time)
|
@unittest.skipUnless(hasattr(signal, 'sigwaitinfo'),
|
||||||
|
'need signal.sigwaitinfo()')
|
||||||
|
def test_sigwaitinfo(self):
|
||||||
|
def wait_func(signum):
|
||||||
|
signal.sigwaitinfo([signum])
|
||||||
|
|
||||||
|
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()")
|
@unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue