mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
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
This commit is contained in:
parent
f474c5a3f3
commit
ba75af7130
6 changed files with 40 additions and 9 deletions
|
@ -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):
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
# 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):
|
||||||
|
|
||||||
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):
|
||||||
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()
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Added multiprocessing.Process.kill method to terminate using the SIGKILL
|
||||||
|
signal on Unix.
|
Loading…
Add table
Add a link
Reference in a new issue