@@ -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
41234147if __name__ == "__main__" :
41244148 unittest .main ()
0 commit comments