Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 6 commits into from Jul 18, 2017
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions Doc/library/multiprocessing.rst
Expand Up @@ -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
cause other processes to deadlock.

.. method:: kill()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a new method, you need a versionadded marker a well (see above the sentinel attribute for an example).


Same as :meth:`terminate()` but using the ``SIGKILL`` signal on Unix.

.. versionadded:: 3.7

.. method:: close()

Close the :class:`Process` object, releasing all resources associated
Expand Down
10 changes: 8 additions & 2 deletions Lib/multiprocessing/popen_fork.py
Expand Up @@ -49,16 +49,22 @@ def wait(self, timeout=None):
return self.poll(os.WNOHANG if timeout == 0.0 else 0)
return self.returncode

def terminate(self):
def _send_signal(self, sig):
if self.returncode is None:
try:
os.kill(self.pid, signal.SIGTERM)
os.kill(self.pid, sig)
except ProcessLookupError:
pass
except OSError:
if self.wait(timeout=0.1) is None:
raise

def terminate(self):
self._send_signal(signal.SIGTERM)

def kill(self):
self._send_signal(signal.SIGKILL)

def _launch(self, process_obj):
code = 1
parent_r, child_w = os.pipe()
Expand Down
2 changes: 2 additions & 0 deletions Lib/multiprocessing/popen_spawn_win32.py
Expand Up @@ -97,5 +97,7 @@ def terminate(self):
if self.wait(timeout=1.0) is None:
raise

kill = terminate

def close(self):
self.finalizer()
7 changes: 7 additions & 0 deletions Lib/multiprocessing/process.py
Expand Up @@ -122,6 +122,13 @@ def terminate(self):
self._check_closed()
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):
'''
Wait until child process terminates
Expand Down
22 changes: 15 additions & 7 deletions Lib/test/_test_multiprocessing.py
Expand Up @@ -277,18 +277,18 @@ def test_process(self):
self.assertNotIn(p, self.active_children())

@classmethod
def _test_terminate(cls):
def _sleep_some(cls):
time.sleep(100)

@classmethod
def _test_sleep(cls, delay):
time.sleep(delay)

def test_terminate(self):
def _kill_process(self, meth):
if self.TYPE == 'threads':
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.start()

Expand All @@ -309,7 +309,7 @@ def test_terminate(self):
# XXX maybe terminating too soon causes the problems on Gentoo...
time.sleep(1)

p.terminate()
meth(p)

if hasattr(signal, 'alarm'):
# On the Gentoo buildbot waitpid() often seems to block forever.
Expand All @@ -333,9 +333,17 @@ def handler(*args):

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':
self.assertEqual(exitcode, -signal.SIGTERM)

def test_kill(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this is duplicating most of test_terminate. Perhaps those two methods could use a shared helper?

exitcode = self._kill_process(multiprocessing.Process.kill)
if os.name != 'nt':
self.assertEqual(p.exitcode, -signal.SIGTERM)
self.assertEqual(exitcode, -signal.SIGKILL)

def test_cpu_count(self):
try:
Expand Down Expand Up @@ -462,7 +470,7 @@ def test_many_processes(self):
for p in procs:
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 p in procs:
p.start()
Expand Down
@@ -0,0 +1,2 @@
Added multiprocessing.Process.kill method to terminate using the SIGKILL
signal on Unix.