diff options
author | Antoine Pitrou <pitrou@free.fr> | 2017-11-03 12:59:43 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-11-03 12:59:43 (GMT) |
commit | 019c99f325287741d1e0eefeef2b75c8e00b884f (patch) | |
tree | 7bd7d25a44ffe1c6c1ee2d09d8845798cc622bf9 /Lib/test | |
parent | 5fbe5e161c969bc8a0d44a301152f8bf5afe0fc7 (diff) | |
download | cpython-019c99f325287741d1e0eefeef2b75c8e00b884f.zip cpython-019c99f325287741d1e0eefeef2b75c8e00b884f.tar.gz cpython-019c99f325287741d1e0eefeef2b75c8e00b884f.tar.bz2 |
[3.6] bpo-31308: If multiprocessing's forkserver dies, launch it again when necessary (GH-3246) (#4252)
* bpo-31308: If multiprocessing's forkserver dies, launch it again when necessary.
* Fix test on Windows
* Add NEWS entry
* Adopt a different approach: ignore SIGINT and SIGTERM, as in semaphore tracker.
* Fix comment
* Make sure the test doesn't muck with process state
* Also test previously-started processes
* Update 2017-08-30-17-59-36.bpo-31308.KbexyC.rst
* Avoid masking SIGTERM in forkserver. It's not necessary and causes a race condition in test_many_processes..
(cherry picked from commit fc6b348b12ad401cab0261b7b71a65c60a08c0a8)
Diffstat (limited to 'Lib/test')
-rw-r--r-- | Lib/test/_test_multiprocessing.py | 48 |
1 files changed, 48 insertions, 0 deletions
diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 80c95d6..056474b 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -446,6 +446,54 @@ class _TestProcess(BaseTestCase): finally: setattr(sys, stream_name, old_stream) + @classmethod + def _sleep_and_set_event(self, evt, delay=0.0): + time.sleep(delay) + evt.set() + + def check_forkserver_death(self, signum): + # bpo-31308: if the forkserver process has died, we should still + # be able to create and run new Process instances (the forkserver + # is implicitly restarted). + if self.TYPE == 'threads': + self.skipTest('test not appropriate for {}'.format(self.TYPE)) + sm = multiprocessing.get_start_method() + if sm != 'forkserver': + # The fork method by design inherits all fds from the parent, + # trying to go against it is a lost battle + self.skipTest('test not appropriate for {}'.format(sm)) + + from multiprocessing.forkserver import _forkserver + _forkserver.ensure_running() + + evt = self.Event() + proc = self.Process(target=self._sleep_and_set_event, args=(evt, 1.0)) + proc.start() + + pid = _forkserver._forkserver_pid + os.kill(pid, signum) + time.sleep(1.0) # give it time to die + + evt2 = self.Event() + proc2 = self.Process(target=self._sleep_and_set_event, args=(evt2,)) + proc2.start() + proc2.join() + self.assertTrue(evt2.is_set()) + self.assertEqual(proc2.exitcode, 0) + + proc.join() + self.assertTrue(evt.is_set()) + self.assertIn(proc.exitcode, (0, 255)) + + def test_forkserver_sigint(self): + # Catchable signal + self.check_forkserver_death(signal.SIGINT) + + def test_forkserver_sigkill(self): + # Uncatchable signal + if os.name != 'nt': + self.check_forkserver_death(signal.SIGKILL) + # # |