Skip to content

Commit f7d090c

Browse files
authored
bpo-30796: Fix failures in signal delivery stress test (#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 beeca6e commit f7d090c

File tree

1 file changed

+50
-15
lines changed

1 file changed

+50
-15
lines changed

Lib/test/test_signal.py

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import random
33
import signal
44
import socket
5+
import statistics
56
import subprocess
67
import sys
78
import time
@@ -949,13 +950,55 @@ class StressTest(unittest.TestCase):
949950
previously tripped signal handlers.
950951
"""
951952

953+
def setsig(self, signum, handler):
954+
old_handler = signal.signal(signum, handler)
955+
self.addCleanup(signal.signal, signum, old_handler)
956+
957+
def measure_itimer_resolution(self):
958+
N = 20
959+
times = []
960+
961+
def handler(signum=None, frame=None):
962+
if len(times) < N:
963+
times.append(time.perf_counter())
964+
# 1 µs is the smallest possible timer interval,
965+
# we want to measure what the concrete duration
966+
# will be on this platform
967+
signal.setitimer(signal.ITIMER_REAL, 1e-6)
968+
969+
self.addCleanup(signal.setitimer, signal.ITIMER_REAL, 0)
970+
self.setsig(signal.SIGALRM, handler)
971+
handler()
972+
while len(times) < N:
973+
time.sleep(1e-3)
974+
975+
durations = [times[i+1] - times[i] for i in range(len(times) - 1)]
976+
med = statistics.median(durations)
977+
if support.verbose:
978+
print("detected median itimer() resolution: %.6f s." % (med,))
979+
return med
980+
981+
def decide_itimer_count(self):
982+
# Some systems have poor setitimer() resolution (for example
983+
# measured around 20 ms. on FreeBSD 9), so decide on a reasonable
984+
# number of sequential timers based on that.
985+
reso = self.measure_itimer_resolution()
986+
if reso <= 1e-4:
987+
return 10000
988+
elif reso <= 1e-2:
989+
return 100
990+
else:
991+
self.skipTest("detected itimer resolution (%.3f s.) too high "
992+
"(> 10 ms.) on this platform (or system too busy)"
993+
% (reso,))
994+
952995
@unittest.skipUnless(hasattr(signal, "setitimer"),
953996
"test needs setitimer()")
954997
def test_stress_delivery_dependent(self):
955998
"""
956999
This test uses dependent signal handlers.
9571000
"""
958-
N = 10000
1001+
N = self.decide_itimer_count()
9591002
sigs = []
9601003

9611004
def first_handler(signum, frame):
@@ -969,16 +1012,12 @@ def first_handler(signum, frame):
9691012
def second_handler(signum=None, frame=None):
9701013
sigs.append(signum)
9711014

972-
def setsig(signum, handler):
973-
old_handler = signal.signal(signum, handler)
974-
self.addCleanup(signal.signal, signum, old_handler)
975-
9761015
# Here on Linux, SIGPROF > SIGALRM > SIGUSR1. By using both
9771016
# ascending and descending sequences (SIGUSR1 then SIGALRM,
9781017
# SIGPROF then SIGALRM), we maximize chances of hitting a bug.
979-
setsig(signal.SIGPROF, first_handler)
980-
setsig(signal.SIGUSR1, first_handler)
981-
setsig(signal.SIGALRM, second_handler) # for ITIMER_REAL
1018+
self.setsig(signal.SIGPROF, first_handler)
1019+
self.setsig(signal.SIGUSR1, first_handler)
1020+
self.setsig(signal.SIGALRM, second_handler) # for ITIMER_REAL
9821021

9831022
expected_sigs = 0
9841023
deadline = time.time() + 15.0
@@ -1005,18 +1044,14 @@ def test_stress_delivery_simultaneous(self):
10051044
"""
10061045
This test uses simultaneous signal handlers.
10071046
"""
1008-
N = 10000
1047+
N = self.decide_itimer_count()
10091048
sigs = []
10101049

10111050
def handler(signum, frame):
10121051
sigs.append(signum)
10131052

1014-
def setsig(signum, handler):
1015-
old_handler = signal.signal(signum, handler)
1016-
self.addCleanup(signal.signal, signum, old_handler)
1017-
1018-
setsig(signal.SIGUSR1, handler)
1019-
setsig(signal.SIGALRM, handler) # for ITIMER_REAL
1053+
self.setsig(signal.SIGUSR1, handler)
1054+
self.setsig(signal.SIGALRM, handler) # for ITIMER_REAL
10201055

10211056
expected_sigs = 0
10221057
deadline = time.time() + 15.0

0 commit comments

Comments
 (0)