mirror of https://github.com/python/cpython.git
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
This commit is contained in:
parent
6c8c2943d9
commit
aa8ec34ad5
|
@ -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."""
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
socketserver.ForkingMixIn.server_close() now waits until all child processes
|
||||
completed to prevent leaking zombie processes.
|
Loading…
Reference in New Issue