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 1 commit
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
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
53 changes: 53 additions & 0 deletions Lib/test/_test_multiprocessing.py
Expand Up @@ -337,6 +337,59 @@ def handler(*args):
if os.name != 'nt':
self.assertEqual(p.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?

if self.TYPE == 'threads':
self.skipTest('test not appropriate for {}'.format(self.TYPE))

p = self.Process(target=self._test_terminate)
p.daemon = True
p.start()

self.assertEqual(p.is_alive(), True)
self.assertIn(p, self.active_children())
self.assertEqual(p.exitcode, None)

join = TimingWrapper(p.join)

self.assertEqual(join(0), None)
self.assertTimingAlmostEqual(join.elapsed, 0.0)
self.assertEqual(p.is_alive(), True)

self.assertEqual(join(-1), None)
self.assertTimingAlmostEqual(join.elapsed, 0.0)
self.assertEqual(p.is_alive(), True)

# XXX maybe terminating too soon causes the problems on Gentoo...
time.sleep(1)

p.kill()

if hasattr(signal, 'alarm'):
# On the Gentoo buildbot waitpid() often seems to block forever.
# We use alarm() to interrupt it if it blocks for too long.
def handler(*args):
raise RuntimeError('join took too long: %s' % p)
old_handler = signal.signal(signal.SIGALRM, handler)
try:
signal.alarm(10)
self.assertEqual(join(), None)
finally:
signal.alarm(0)
signal.signal(signal.SIGALRM, old_handler)
else:
self.assertEqual(join(), None)

self.assertTimingAlmostEqual(join.elapsed, 0.0)

self.assertEqual(p.is_alive(), False)
self.assertNotIn(p, self.active_children())

p.join()

# sometimes get p.exitcode == 0 on Windows ...
if os.name != 'nt':
self.assertEqual(p.exitcode, -signal.SIGKILL)

def test_cpu_count(self):
try:
cpus = multiprocessing.cpu_count()
Expand Down