summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2017-08-10 14:01:47 (GMT)
committerGitHub <noreply@github.com>2017-08-10 14:01:47 (GMT)
commitb5011479808b80545bdd1246725fc7940691b084 (patch)
treed173af1620fb279835d0a134ac164bd3eb46d557
parentaa8ec34ad52bb3b274ce91169e1bc4a598655049 (diff)
downloadcpython-b5011479808b80545bdd1246725fc7940691b084.zip
cpython-b5011479808b80545bdd1246725fc7940691b084.tar.gz
cpython-b5011479808b80545bdd1246725fc7940691b084.tar.bz2
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
-rw-r--r--Lib/test/support/__init__.py33
-rw-r--r--Lib/test/test_support.py57
2 files changed, 73 insertions, 17 deletions
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index 7a4671c..0235498 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -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):
diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py
index 1e6b2b5..8632837 100644
--- a/Lib/test/test_support.py
+++ b/Lib/test/test_support.py
@@ -1,12 +1,15 @@
+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