Enhance support.reap_children() (#3036)

* reap_children() now sets environment_altered to True to detect bugs
  using python3 -m test --fail-env-changed
* Replace bare "except:" with "except OSError:" in reap_children()
* Write an unit test for reap_children() using a timeout of 60
  seconds
This commit is contained in:
Victor Stinner 2017-08-10 16:01:47 +02:00 committed by GitHub
parent aa8ec34ad5
commit b501147980
2 changed files with 73 additions and 17 deletions

View File

@ -2073,26 +2073,35 @@ def reap_threads(func):
threading_cleanup(*key) threading_cleanup(*key)
return decorator return decorator
def reap_children(): def reap_children():
"""Use this function at the end of test_main() whenever sub-processes """Use this function at the end of test_main() whenever sub-processes
are started. This will help ensure that no extra children (zombies) are started. This will help ensure that no extra children (zombies)
stick around to hog resources and create problems when looking stick around to hog resources and create problems when looking
for refleaks. for refleaks.
""" """
global environment_altered
# Need os.waitpid(-1, os.WNOHANG): Windows is not supported
if not (hasattr(os, 'waitpid') and hasattr(os, 'WNOHANG')):
return
# Reap all our dead child processes so we don't leave zombies around. # Reap all our dead child processes so we don't leave zombies around.
# These hog resources and might be causing some of the buildbots to die. # These hog resources and might be causing some of the buildbots to die.
if hasattr(os, 'waitpid'):
any_process = -1
while True: while True:
try: try:
# This will raise an exception on Windows. That's ok. # Read the exit status of any child process which already completed
pid, status = os.waitpid(any_process, os.WNOHANG) pid, status = os.waitpid(-1, os.WNOHANG)
except OSError:
break
if pid == 0: if pid == 0:
break break
print("Warning -- reap_children() reaped child process %s" print("Warning -- reap_children() reaped child process %s"
% pid, file=sys.stderr) % pid, file=sys.stderr)
except: environment_altered = True
break
@contextlib.contextmanager @contextlib.contextmanager
def start_threads(threads, unlock=None): def start_threads(threads, unlock=None):

View File

@ -1,12 +1,15 @@
import contextlib
import errno
import importlib import importlib
import io
import os
import shutil import shutil
import socket
import stat import stat
import sys import sys
import os
import unittest
import socket
import tempfile import tempfile
import errno import time
import unittest
from test import support from test import support
TESTFN = support.TESTFN TESTFN = support.TESTFN
@ -378,6 +381,51 @@ class TestSupport(unittest.TestCase):
self.assertRaises(AssertionError, support.check__all__, self, unittest) self.assertRaises(AssertionError, support.check__all__, self, unittest)
@unittest.skipUnless(hasattr(os, 'waitpid') and hasattr(os, 'WNOHANG'),
'need os.waitpid() and os.WNOHANG')
def test_reap_children(self):
# Make sure that there is no other pending child process
support.reap_children()
# Create a child process
pid = os.fork()
if pid == 0:
# child process: do nothing, just exit
os._exit(0)
t0 = time.monotonic()
deadline = time.monotonic() + 60.0
was_altered = support.environment_altered
try:
support.environment_altered = False
stderr = io.StringIO()
while True:
if time.monotonic() > deadline:
self.fail("timeout")
with contextlib.redirect_stderr(stderr):
support.reap_children()
# Use environment_altered to check if reap_children() found
# the child process
if support.environment_altered:
break
# loop until the child process completed
time.sleep(0.100)
msg = "Warning -- reap_children() reaped child process %s" % pid
self.assertIn(msg, stderr.getvalue())
self.assertTrue(support.environment_altered)
finally:
support.environment_altered = was_altered
# Just in case, check again that there is no other
# pending child process
support.reap_children()
# XXX -follows a list of untested API # XXX -follows a list of untested API
# make_legacy_pyc # make_legacy_pyc
# is_resource_enabled # is_resource_enabled
@ -398,7 +446,6 @@ class TestSupport(unittest.TestCase):
# run_doctest # run_doctest
# threading_cleanup # threading_cleanup
# reap_threads # reap_threads
# reap_children
# strip_python_stderr # strip_python_stderr
# args_from_interpreter_flags # args_from_interpreter_flags
# can_symlink # can_symlink