Skip to content

Commit 0eb63eb

Browse files
committed
bpo-30796: Fix failures in signal delivery stress test (python#2488)
* bpo-30796: Fix failures in signal delivery stress test setitimer() can have a poor minimum resolution on some machines, this would make the test reach its deadline (and a stray signal could then kill a subsequent test). * Make sure to clear the itimer after the test
1 parent 0879adb commit 0eb63eb

1 file changed

Lines changed: 50 additions & 16 deletions

File tree

‎Lib/test/test_signal.py‎

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import select
1010
import signal
1111
import socket
12-
import struct
12+
import statistics
1313
import subprocess
1414
import traceback
1515
import sys, os, time, errno
@@ -964,13 +964,55 @@ class StressTest(unittest.TestCase):
964964
previously tripped signal handlers.
965965
"""
966966

967+
def setsig(self, signum, handler):
968+
old_handler = signal.signal(signum, handler)
969+
self.addCleanup(signal.signal, signum, old_handler)
970+
971+
def measure_itimer_resolution(self):
972+
N = 20
973+
times = []
974+
975+
def handler(signum=None, frame=None):
976+
if len(times) < N:
977+
times.append(time.perf_counter())
978+
# 1 µs is the smallest possible timer interval,
979+
# we want to measure what the concrete duration
980+
# will be on this platform
981+
signal.setitimer(signal.ITIMER_REAL, 1e-6)
982+
983+
self.addCleanup(signal.setitimer, signal.ITIMER_REAL, 0)
984+
self.setsig(signal.SIGALRM, handler)
985+
handler()
986+
while len(times) < N:
987+
time.sleep(1e-3)
988+
989+
durations = [times[i+1] - times[i] for i in range(len(times) - 1)]
990+
med = statistics.median(durations)
991+
if support.verbose:
992+
print("detected median itimer() resolution: %.6f s." % (med,))
993+
return med
994+
995+
def decide_itimer_count(self):
996+
# Some systems have poor setitimer() resolution (for example
997+
# measured around 20 ms. on FreeBSD 9), so decide on a reasonable
998+
# number of sequential timers based on that.
999+
reso = self.measure_itimer_resolution()
1000+
if reso <= 1e-4:
1001+
return 10000
1002+
elif reso <= 1e-2:
1003+
return 100
1004+
else:
1005+
self.skipTest("detected itimer resolution (%.3f s.) too high "
1006+
"(> 10 ms.) on this platform (or system too busy)"
1007+
% (reso,))
1008+
9671009
@unittest.skipUnless(hasattr(signal, "setitimer"),
9681010
"test needs setitimer()")
9691011
def test_stress_delivery_dependent(self):
9701012
"""
9711013
This test uses dependent signal handlers.
9721014
"""
973-
N = 10000
1015+
N = self.decide_itimer_count()
9741016
sigs = []
9751017

9761018
def first_handler(signum, frame):
@@ -984,16 +1026,12 @@ def first_handler(signum, frame):
9841026
def second_handler(signum=None, frame=None):
9851027
sigs.append(signum)
9861028

987-
def setsig(signum, handler):
988-
old_handler = signal.signal(signum, handler)
989-
self.addCleanup(signal.signal, signum, old_handler)
990-
9911029
# Here on Linux, SIGPROF > SIGALRM > SIGUSR1. By using both
9921030
# ascending and descending sequences (SIGUSR1 then SIGALRM,
9931031
# SIGPROF then SIGALRM), we maximize chances of hitting a bug.
994-
setsig(signal.SIGPROF, first_handler)
995-
setsig(signal.SIGUSR1, first_handler)
996-
setsig(signal.SIGALRM, second_handler) # for ITIMER_REAL
1032+
self.setsig(signal.SIGPROF, first_handler)
1033+
self.setsig(signal.SIGUSR1, first_handler)
1034+
self.setsig(signal.SIGALRM, second_handler) # for ITIMER_REAL
9971035

9981036
expected_sigs = 0
9991037
deadline = time.time() + 15.0
@@ -1020,18 +1058,14 @@ def test_stress_delivery_simultaneous(self):
10201058
"""
10211059
This test uses simultaneous signal handlers.
10221060
"""
1023-
N = 10000
1061+
N = self.decide_itimer_count()
10241062
sigs = []
10251063

10261064
def handler(signum, frame):
10271065
sigs.append(signum)
10281066

1029-
def setsig(signum, handler):
1030-
old_handler = signal.signal(signum, handler)
1031-
self.addCleanup(signal.signal, signum, old_handler)
1032-
1033-
setsig(signal.SIGUSR1, handler)
1034-
setsig(signal.SIGALRM, handler) # for ITIMER_REAL
1067+
self.setsig(signal.SIGUSR1, handler)
1068+
self.setsig(signal.SIGALRM, handler) # for ITIMER_REAL
10351069

10361070
expected_sigs = 0
10371071
deadline = time.time() + 15.0

0 commit comments

Comments
 (0)