Skip to content

Commit 296e572

Browse files
authored
[3.7] bpo-34130: Fix 2 race conditions in test_signal (GH-8329)
* bpo-34130: Fix test_signal.test_socket() (GH-8326) test_signal.test_socket(): On Windows, sometimes even if the C signal handler succeed to write the signal number into the write end of the socketpair, the test fails with a BlockingIOError on the non-blocking read.recv(1) because the read end of the socketpair didn't receive the byte yet. Fix the race condition on Windows by setting the read end as blocking. (cherry picked from commit 99bb6df) * bpo-34130: Fix test_signal.test_warn_on_full_buffer() (GH-8327) On Windows, sometimes test_signal.test_warn_on_full_buffer() fails to fill the socketpair buffer. In that case, the C signal handler succeed to write into the socket, it doesn't log the expected send error, and so the test fail. On Windows, the test now uses a timeout of 50 ms to fill the socketpair buffer to fix this race condition. Other changes: * Begin with large chunk size to fill the buffer to speed up the test. * Add error messages to assertion errors to more easily identify which assertion failed. * Don't set the read end of the socketpair as non-blocking. (cherry picked from commit 686b4b5)
1 parent 68833dc commit 296e572

File tree

1 file changed

+41
-16
lines changed

1 file changed

+41
-16
lines changed

Lib/test/test_signal.py

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,6 @@ def handler(signum, frame):
367367
signal.signal(signum, handler)
368368
369369
read, write = socket.socketpair()
370-
read.setblocking(False)
371370
write.setblocking(False)
372371
signal.set_wakeup_fd(write.fileno())
373372
@@ -455,26 +454,51 @@ def handler(signum, frame):
455454
signal.signal(signum, handler)
456455
457456
read, write = socket.socketpair()
458-
read.setblocking(False)
459-
write.setblocking(False)
460457
461-
# Fill the send buffer
458+
# Fill the socketpair buffer
459+
if sys.platform == 'win32':
460+
# bpo-34130: On Windows, sometimes non-blocking send fails to fill
461+
# the full socketpair buffer, so use a timeout of 50 ms instead.
462+
write.settimeout(0.050)
463+
else:
464+
write.setblocking(False)
465+
466+
# Start with large chunk size to reduce the
467+
# number of send needed to fill the buffer.
468+
written = 0
469+
for chunk_size in (2 ** 16, 2 ** 8, 1):
470+
chunk = b"x" * chunk_size
471+
try:
472+
while True:
473+
write.send(chunk)
474+
written += chunk_size
475+
except (BlockingIOError, socket.timeout):
476+
pass
477+
478+
print(f"%s bytes written into the socketpair" % written, flush=True)
479+
480+
write.setblocking(False)
462481
try:
463-
while True:
464-
write.send(b"x")
482+
write.send(b"x")
465483
except BlockingIOError:
484+
# The socketpair buffer seems full
466485
pass
486+
else:
487+
raise AssertionError("%s bytes failed to fill the socketpair "
488+
"buffer" % written)
467489
468490
# By default, we get a warning when a signal arrives
491+
msg = ('Exception ignored when trying to {action} '
492+
'to the signal wakeup fd')
469493
signal.set_wakeup_fd(write.fileno())
470494
471495
with captured_stderr() as err:
472496
_testcapi.raise_signal(signum)
473497
474498
err = err.getvalue()
475-
if ('Exception ignored when trying to {action} to the signal wakeup fd'
476-
not in err):
477-
raise AssertionError(err)
499+
if msg not in err:
500+
raise AssertionError("first set_wakeup_fd() test failed, "
501+
"stderr: %r" % err)
478502
479503
# And also if warn_on_full_buffer=True
480504
signal.set_wakeup_fd(write.fileno(), warn_on_full_buffer=True)
@@ -483,9 +507,9 @@ def handler(signum, frame):
483507
_testcapi.raise_signal(signum)
484508
485509
err = err.getvalue()
486-
if ('Exception ignored when trying to {action} to the signal wakeup fd'
487-
not in err):
488-
raise AssertionError(err)
510+
if msg not in err:
511+
raise AssertionError("set_wakeup_fd(warn_on_full_buffer=True) "
512+
"test failed, stderr: %r" % err)
489513
490514
# But not if warn_on_full_buffer=False
491515
signal.set_wakeup_fd(write.fileno(), warn_on_full_buffer=False)
@@ -495,7 +519,8 @@ def handler(signum, frame):
495519
496520
err = err.getvalue()
497521
if err != "":
498-
raise AssertionError("got unexpected output %r" % (err,))
522+
raise AssertionError("set_wakeup_fd(warn_on_full_buffer=False) "
523+
"test failed, stderr: %r" % err)
499524
500525
# And then check the default again, to make sure warn_on_full_buffer
501526
# settings don't leak across calls.
@@ -505,9 +530,9 @@ def handler(signum, frame):
505530
_testcapi.raise_signal(signum)
506531
507532
err = err.getvalue()
508-
if ('Exception ignored when trying to {action} to the signal wakeup fd'
509-
not in err):
510-
raise AssertionError(err)
533+
if msg not in err:
534+
raise AssertionError("second set_wakeup_fd() test failed, "
535+
"stderr: %r" % err)
511536
512537
""".format(action=action)
513538
assert_python_ok('-c', code)

0 commit comments

Comments
 (0)