summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
authorAntoine Pitrou <pitrou@free.fr>2017-11-03 12:59:43 (GMT)
committerGitHub <noreply@github.com>2017-11-03 12:59:43 (GMT)
commit019c99f325287741d1e0eefeef2b75c8e00b884f (patch)
tree7bd7d25a44ffe1c6c1ee2d09d8845798cc622bf9 /Lib/test
parent5fbe5e161c969bc8a0d44a301152f8bf5afe0fc7 (diff)
downloadcpython-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.py48
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)
+
#
#