Skip to content

Commit 5f1df08

Browse files
committed
bpo-33613, test_semaphore_tracker_sigint: fix race condition
Fail `test_semaphore_tracker_sigint` if no warnings are expected and one is received. Fix race condition when the child receives SIGINT before it can register signal handlers for it. The race condition occurs when the parent calls `_semaphore_tracker.ensure_running()` (which in turn spawns the semaphore_tracker using `_posixsubprocess.fork_exec`), the child registers the signal handlers and the parent tries to kill the child. What seem to happen is that in some slow systems, the parent sends the signal to kill the child before the child protects against the signal. There is no reliable and portable solution for the parent to wait until the child has register the signal handlers to send the signal to kill the child so a `sleep` is introduced between the spawning of the child and the parent sending the signal to give time to the child to register the handlers.
1 parent 8534d53 commit 5f1df08

File tree

1 file changed

+11
-5
lines changed

1 file changed

+11
-5
lines changed

Lib/test/_test_multiprocessing.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import struct
2121
import operator
2222
import weakref
23+
import warnings
2324
import test.support
2425
import test.support.script_helper
2526
from test import support
@@ -4474,15 +4475,12 @@ def check_semaphore_tracker_death(self, signum, should_die):
44744475
from multiprocessing.semaphore_tracker import _semaphore_tracker
44754476
_semaphore_tracker.ensure_running()
44764477
pid = _semaphore_tracker._pid
4478+
time.sleep(1.0) # Give time for the child to register the signal handlers
44774479
os.kill(pid, signum)
44784480
time.sleep(1.0) # give it time to die
44794481

44804482
ctx = multiprocessing.get_context("spawn")
4481-
with contextlib.ExitStack() as stack:
4482-
if should_die:
4483-
stack.enter_context(self.assertWarnsRegex(
4484-
UserWarning,
4485-
"semaphore_tracker: process died"))
4483+
with warnings.catch_warnings(record=True) as all_warn:
44864484
sem = ctx.Semaphore()
44874485
sem.acquire()
44884486
sem.release()
@@ -4492,6 +4490,14 @@ def check_semaphore_tracker_death(self, signum, should_die):
44924490
del sem
44934491
gc.collect()
44944492
self.assertIsNone(wr())
4493+
if should_die:
4494+
self.assertEqual(len(all_warn), 1)
4495+
the_warn = all_warn[0]
4496+
issubclass(the_warn.category, UserWarning)
4497+
self.assertTrue("semaphore_tracker: process died"
4498+
in str(the_warn.message))
4499+
else:
4500+
self.assertEqual(len(all_warn), 0)
44954501

44964502
def test_semaphore_tracker_sigint(self):
44974503
# Catchable signal (ignored by semaphore tracker)

0 commit comments

Comments
 (0)