Commit b5011479 authored by Victor Stinner's avatar Victor Stinner Committed by GitHub

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
parent aa8ec34a
......@@ -2073,26 +2073,35 @@ def reap_threads(func):
threading_cleanup(*key)
return decorator
def reap_children():
"""Use this function at the end of test_main() whenever sub-processes
are started. This will help ensure that no extra children (zombies)
stick around to hog resources and create problems when looking
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.
# These hog resources and might be causing some of the buildbots to die.
if hasattr(os, 'waitpid'):
any_process = -1
while True:
try:
# This will raise an exception on Windows. That's ok.
pid, status = os.waitpid(any_process, os.WNOHANG)
if pid == 0:
break
print("Warning -- reap_children() reaped child process %s"
% pid, file=sys.stderr)
except:
break
while True:
try:
# Read the exit status of any child process which already completed
pid, status = os.waitpid(-1, os.WNOHANG)
except OSError:
break
if pid == 0:
break
print("Warning -- reap_children() reaped child process %s"
% pid, file=sys.stderr)
environment_altered = True
@contextlib.contextmanager
def start_threads(threads, unlock=None):
......
import contextlib
import errno
import importlib
import io
import os
import shutil
import socket
import stat
import sys
import os
import unittest
import socket
import tempfile
import errno
import time
import unittest
from test import support
TESTFN = support.TESTFN
......@@ -378,6 +381,51 @@ class TestSupport(unittest.TestCase):
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
# make_legacy_pyc
# is_resource_enabled
......@@ -398,7 +446,6 @@ class TestSupport(unittest.TestCase):
# run_doctest
# threading_cleanup
# reap_threads
# reap_children
# strip_python_stderr
# args_from_interpreter_flags
# can_symlink
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment