Fix bpo-30589: improve Process.exitcode with forkserver (#1989) · LDM2020/cpython@dfd5f34 · GitHub
Skip to content

Commit dfd5f34

Browse files
authored
Fix bpo-30589: improve Process.exitcode with forkserver (python#1989)
* Fix bpo-30589: improve Process.exitcode with forkserver When the child is killed, Process.exitcode should return -signum, not 255. * Add Misc/NEWS
1 parent ced36a9 commit dfd5f34

5 files changed

Lines changed: 138 additions & 49 deletions

File tree

Lib/multiprocessing/forkserver.py

Lines changed: 86 additions & 35 deletions

Lib/multiprocessing/popen_fork.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,12 @@ def duplicate_for_child(self, fd):
2424

2525
def poll(self, flag=os.WNOHANG):
2626
if self.returncode is None:
27-
while True:
28-
try:
29-
pid, sts = os.waitpid(self.pid, flag)
30-
except OSError as e:
31-
# Child process not yet created. See #1731717
32-
# e.errno == errno.ECHILD == 10
33-
return None
34-
else:
35-
break
27+
try:
28+
pid, sts = os.waitpid(self.pid, flag)
29+
except OSError as e:
30+
# Child process not yet created. See #1731717
31+
# e.errno == errno.ECHILD == 10
32+
return None
3633
if pid == self.pid:
3734
if os.WIFSIGNALED(sts):
3835
self.returncode = -os.WTERMSIG(sts)

Lib/multiprocessing/popen_forkserver.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def _launch(self, process_obj):
5252
util.Finalize(self, os.close, (self.sentinel,))
5353
with open(w, 'wb', closefd=True) as f:
5454
f.write(buf.getbuffer())
55-
self.pid = forkserver.read_unsigned(self.sentinel)
55+
self.pid = forkserver.read_signed(self.sentinel)
5656

5757
def poll(self, flag=os.WNOHANG):
5858
if self.returncode is None:
@@ -61,8 +61,10 @@ def poll(self, flag=os.WNOHANG):
6161
if not wait([self.sentinel], timeout):
6262
return None
6363
try:
64-
self.returncode = forkserver.read_unsigned(self.sentinel)
64+
self.returncode = forkserver.read_signed(self.sentinel)
6565
except (OSError, EOFError):
66-
# The process ended abnormally perhaps because of a signal
66+
# This should not happen usually, but perhaps the forkserver
67+
# process itself got killed
6768
self.returncode = 255
69+
6870
return self.returncode

Lib/test/_test_multiprocessing.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,10 @@ def test_process(self):
274274
def _test_terminate(cls):
275275
time.sleep(100)
276276

277+
@classmethod
278+
def _test_sleep(cls, delay):
279+
time.sleep(delay)
280+
277281
def test_terminate(self):
278282
if self.TYPE == 'threads':
279283
self.skipTest('test not appropriate for {}'.format(self.TYPE))
@@ -323,8 +327,9 @@ def handler(*args):
323327

324328
p.join()
325329

326-
# XXX sometimes get p.exitcode == 0 on Windows ...
327-
#self.assertEqual(p.exitcode, -signal.SIGTERM)
330+
# sometimes get p.exitcode == 0 on Windows ...
331+
if os.name != 'nt':
332+
self.assertEqual(p.exitcode, -signal.SIGTERM)
328333

329334
def test_cpu_count(self):
330335
try:
@@ -398,6 +403,36 @@ def test_sentinel(self):
398403
p.join()
399404
self.assertTrue(wait_for_handle(sentinel, timeout=1))
400405

406+
def test_many_processes(self):
407+
if self.TYPE == 'threads':
408+
self.skipTest('test not appropriate for {}'.format(self.TYPE))
409+
410+
sm = multiprocessing.get_start_method()
411+
N = 5 if sm == 'spawn' else 100
412+
413+
# Try to overwhelm the forkserver loop with events
414+
procs = [self.Process(target=self._test_sleep, args=(0.01,))
415+
for i in range(N)]
416+
for p in procs:
417+
p.start()
418+
for p in procs:
419+
p.join(timeout=10)
420+
for p in procs:
421+
self.assertEqual(p.exitcode, 0)
422+
423+
procs = [self.Process(target=self._test_terminate)
424+
for i in range(N)]
425+
for p in procs:
426+
p.start()
427+
time.sleep(0.001) # let the children start...
428+
for p in procs:
429+
p.terminate()
430+
for p in procs:
431+
p.join(timeout=10)
432+
if os.name != 'nt':
433+
for p in procs:
434+
self.assertEqual(p.exitcode, -signal.SIGTERM)
435+
401436
#
402437
#
403438
#

Misc/NEWS

Lines changed: 4 additions & 0 deletions

0 commit comments

Comments
 (0)