[Python-checkins] bpo-31151: Add socketserver.ForkingMixIn.server_close() (#3057)
Victor Stinner
webhook-mailer at python.org
Thu Aug 10 09:28:19 EDT 2017
https://github.com/python/cpython/commit/aa8ec34ad52bb3b274ce91169e1bc4a598655049
commit: aa8ec34ad52bb3b274ce91169e1bc4a598655049
branch: master
author: Victor Stinner <victor.stinner at gmail.com>
committer: GitHub <noreply at github.com>
date: 2017-08-10T15:28:16+02:00
summary:
bpo-31151: Add socketserver.ForkingMixIn.server_close() (#3057)
* Add socketserver.ForkingMixIn.server_close()
bpo-31151: socketserver.ForkingMixIn.server_close() now waits until
all child processes completed to prevent leaking zombie processes.
* Fix test on Windows which doesn't have ForkingMixIn
files:
A Misc/NEWS.d/next/Library/2017-08-10-13-20-02.bpo-31151.730VBI.rst
M Lib/socketserver.py
M Lib/test/test_socketserver.py
diff --git a/Lib/socketserver.py b/Lib/socketserver.py
index 6e1ae9fddda..df171149501 100644
--- a/Lib/socketserver.py
+++ b/Lib/socketserver.py
@@ -547,7 +547,7 @@ class ForkingMixIn:
active_children = None
max_children = 40
- def collect_children(self):
+ def collect_children(self, *, blocking=False):
"""Internal routine to wait for children that have exited."""
if self.active_children is None:
return
@@ -571,7 +571,8 @@ def collect_children(self):
# Now reap all defunct children.
for pid in self.active_children.copy():
try:
- pid, _ = os.waitpid(pid, os.WNOHANG)
+ flags = 0 if blocking else os.WNOHANG
+ pid, _ = os.waitpid(pid, flags)
# if the child hasn't exited yet, pid will be 0 and ignored by
# discard() below
self.active_children.discard(pid)
@@ -620,6 +621,10 @@ def process_request(self, request, client_address):
finally:
os._exit(status)
+ def server_close(self):
+ super().server_close()
+ self.collect_children(blocking=True)
+
class ThreadingMixIn:
"""Mix-in class to handle each request in a new thread."""
diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py
index 140a6abf9ef..3d93566607d 100644
--- a/Lib/test/test_socketserver.py
+++ b/Lib/test/test_socketserver.py
@@ -144,6 +144,10 @@ def run_server(self, svrcls, hdlrbase, testfunc):
t.join()
server.server_close()
self.assertEqual(-1, server.socket.fileno())
+ if HAVE_FORKING and isinstance(server, socketserver.ForkingMixIn):
+ # bpo-31151: Check that ForkingMixIn.server_close() waits until
+ # all children completed
+ self.assertFalse(server.active_children)
if verbose: print("done")
def stream_examine(self, proto, addr):
@@ -371,10 +375,7 @@ def wait_done(self):
if HAVE_FORKING:
class ForkingErrorTestServer(socketserver.ForkingMixIn, BaseErrorTestServer):
- def wait_done(self):
- [child] = self.active_children
- os.waitpid(child, 0)
- self.active_children.clear()
+ pass
class SocketWriterTest(unittest.TestCase):
diff --git a/Misc/NEWS.d/next/Library/2017-08-10-13-20-02.bpo-31151.730VBI.rst b/Misc/NEWS.d/next/Library/2017-08-10-13-20-02.bpo-31151.730VBI.rst
new file mode 100644
index 00000000000..6eff63653ab
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2017-08-10-13-20-02.bpo-31151.730VBI.rst
@@ -0,0 +1,2 @@
+socketserver.ForkingMixIn.server_close() now waits until all child processes
+completed to prevent leaking zombie processes.
More information about the Python-checkins
mailing list