Skip to content

Commit 932ae58

Browse files
committed
Add test for terminated PID race
1 parent 3c8e603 commit 932ae58

1 file changed

Lines changed: 25 additions & 1 deletion

File tree

Lib/test/test_subprocess.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4104,7 +4104,7 @@ def assert_fast_waitpid_error(self, patch_point):
41044104
with mock.patch(patch_point, side_effect=exc) as m:
41054105
p = subprocess.Popen([sys.executable,
41064106
"-c", "import time; time.sleep(0.3)"])
4107-
with self.assertRaises(subprocess.TimeoutExpired) as c:
4107+
with self.assertRaises(subprocess.TimeoutExpired):
41084108
p.wait(timeout=0.0001)
41094109
self.assertEqual(p.wait(timeout=support.SHORT_TIMEOUT), 0)
41104110
assert m.called
@@ -4119,6 +4119,30 @@ def test_wait_pidfd_open_error(self, patch_point="os.pidfd_open"):
41194119
def test_wait_kqueue_error(self, patch_point="os.pidfd_open"):
41204120
self.assert_fast_waitpid_error("select.kqueue")
41214121

4122+
# ---
4123+
4124+
def assert_wait_pid_race(self, patch_target, real_func):
4125+
# Call pidfd_open() / kqueue, then terminate the process. Make
4126+
# sure that the next poll() / kqueue.control() call still works
4127+
# for a terminated PID.
4128+
p = subprocess.Popen([sys.executable,
4129+
"-c", "import time; time.sleep(0.3)"])
4130+
4131+
def wrapper(*args, **kwargs):
4132+
ret = real_func(*args, **kwargs)
4133+
os.kill(p.pid, signal.SIGTERM)
4134+
return ret
4135+
4136+
with mock.patch(patch_target, side_effect=wrapper) as m:
4137+
with self.assertRaises(subprocess.TimeoutExpired):
4138+
p.wait(timeout=0.0001)
4139+
assert m.called
4140+
self.assertEqual(p.wait(timeout=support.SHORT_TIMEOUT), -signal.SIGTERM)
4141+
4142+
@unittest.skipIf(not hasattr(os, "pidfd_open"), reason="LINUX only")
4143+
def test_pidfd_open_race(self):
4144+
self.assert_wait_pid_race("os.pidfd_open", os.pidfd_open)
4145+
41224146

41234147
if __name__ == "__main__":
41244148
unittest.main()

0 commit comments

Comments
 (0)