Commit ba75af71 authored by Vitor Pereira's avatar Vitor Pereira Committed by Antoine Pitrou

bpo-30794: added kill() method to multiprocessing.Process (#2528)

* bpo-30794: added kill() method to multiprocessing.Process

* Added entries to documentation and NEWS

* Refactored test_terminate and test_kill

* Fix SIGTERM and SIGKILL being used on Windows for the tests

* Added "versionadded" marker to the documentation

* Fix trailing whitespace in doc
parent f474c5a3
...@@ -598,6 +598,12 @@ The :mod:`multiprocessing` package mostly replicates the API of the ...@@ -598,6 +598,12 @@ The :mod:`multiprocessing` package mostly replicates the API of the
acquired a lock or semaphore etc. then terminating it is liable to acquired a lock or semaphore etc. then terminating it is liable to
cause other processes to deadlock. cause other processes to deadlock.
.. method:: kill()
Same as :meth:`terminate()` but using the ``SIGKILL`` signal on Unix.
.. versionadded:: 3.7
.. method:: close() .. method:: close()
Close the :class:`Process` object, releasing all resources associated Close the :class:`Process` object, releasing all resources associated
......
...@@ -49,16 +49,22 @@ class Popen(object): ...@@ -49,16 +49,22 @@ class Popen(object):
return self.poll(os.WNOHANG if timeout == 0.0 else 0) return self.poll(os.WNOHANG if timeout == 0.0 else 0)
return self.returncode return self.returncode
def terminate(self): def _send_signal(self, sig):
if self.returncode is None: if self.returncode is None:
try: try:
os.kill(self.pid, signal.SIGTERM) os.kill(self.pid, sig)
except ProcessLookupError: except ProcessLookupError:
pass pass
except OSError: except OSError:
if self.wait(timeout=0.1) is None: if self.wait(timeout=0.1) is None:
raise raise
def terminate(self):
self._send_signal(signal.SIGTERM)
def kill(self):
self._send_signal(signal.SIGKILL)
def _launch(self, process_obj): def _launch(self, process_obj):
code = 1 code = 1
parent_r, child_w = os.pipe() parent_r, child_w = os.pipe()
......
...@@ -97,5 +97,7 @@ class Popen(object): ...@@ -97,5 +97,7 @@ class Popen(object):
if self.wait(timeout=1.0) is None: if self.wait(timeout=1.0) is None:
raise raise
kill = terminate
def close(self): def close(self):
self.finalizer() self.finalizer()
...@@ -122,6 +122,13 @@ class BaseProcess(object): ...@@ -122,6 +122,13 @@ class BaseProcess(object):
self._check_closed() self._check_closed()
self._popen.terminate() self._popen.terminate()
def kill(self):
'''
Terminate process; sends SIGKILL signal or uses TerminateProcess()
'''
self._check_closed()
self._popen.kill()
def join(self, timeout=None): def join(self, timeout=None):
''' '''
Wait until child process terminates Wait until child process terminates
......
...@@ -277,18 +277,18 @@ class _TestProcess(BaseTestCase): ...@@ -277,18 +277,18 @@ class _TestProcess(BaseTestCase):
self.assertNotIn(p, self.active_children()) self.assertNotIn(p, self.active_children())
@classmethod @classmethod
def _test_terminate(cls): def _sleep_some(cls):
time.sleep(100) time.sleep(100)
@classmethod @classmethod
def _test_sleep(cls, delay): def _test_sleep(cls, delay):
time.sleep(delay) time.sleep(delay)
def test_terminate(self): def _kill_process(self, meth):
if self.TYPE == 'threads': if self.TYPE == 'threads':
self.skipTest('test not appropriate for {}'.format(self.TYPE)) self.skipTest('test not appropriate for {}'.format(self.TYPE))
p = self.Process(target=self._test_terminate) p = self.Process(target=self._sleep_some)
p.daemon = True p.daemon = True
p.start() p.start()
...@@ -309,7 +309,7 @@ class _TestProcess(BaseTestCase): ...@@ -309,7 +309,7 @@ class _TestProcess(BaseTestCase):
# XXX maybe terminating too soon causes the problems on Gentoo... # XXX maybe terminating too soon causes the problems on Gentoo...
time.sleep(1) time.sleep(1)
p.terminate() meth(p)
if hasattr(signal, 'alarm'): if hasattr(signal, 'alarm'):
# On the Gentoo buildbot waitpid() often seems to block forever. # On the Gentoo buildbot waitpid() often seems to block forever.
...@@ -333,9 +333,17 @@ class _TestProcess(BaseTestCase): ...@@ -333,9 +333,17 @@ class _TestProcess(BaseTestCase):
p.join() p.join()
# sometimes get p.exitcode == 0 on Windows ... return p.exitcode
def test_terminate(self):
exitcode = self._kill_process(multiprocessing.Process.terminate)
if os.name != 'nt': if os.name != 'nt':
self.assertEqual(p.exitcode, -signal.SIGTERM) self.assertEqual(exitcode, -signal.SIGTERM)
def test_kill(self):
exitcode = self._kill_process(multiprocessing.Process.kill)
if os.name != 'nt':
self.assertEqual(exitcode, -signal.SIGKILL)
def test_cpu_count(self): def test_cpu_count(self):
try: try:
...@@ -462,7 +470,7 @@ class _TestProcess(BaseTestCase): ...@@ -462,7 +470,7 @@ class _TestProcess(BaseTestCase):
for p in procs: for p in procs:
self.assertEqual(p.exitcode, 0) self.assertEqual(p.exitcode, 0)
procs = [self.Process(target=self._test_terminate) procs = [self.Process(target=self._sleep_some)
for i in range(N)] for i in range(N)]
for p in procs: for p in procs:
p.start() p.start()
......
Added multiprocessing.Process.kill method to terminate using the SIGKILL
signal on Unix.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment