Kaydet (Commit) aa8ec34a authored tarafından Victor Stinner's avatar Victor Stinner Kaydeden (comit) GitHub

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
üst 6c8c2943
...@@ -547,7 +547,7 @@ if hasattr(os, "fork"): ...@@ -547,7 +547,7 @@ if hasattr(os, "fork"):
active_children = None active_children = None
max_children = 40 max_children = 40
def collect_children(self): def collect_children(self, *, blocking=False):
"""Internal routine to wait for children that have exited.""" """Internal routine to wait for children that have exited."""
if self.active_children is None: if self.active_children is None:
return return
...@@ -571,7 +571,8 @@ if hasattr(os, "fork"): ...@@ -571,7 +571,8 @@ if hasattr(os, "fork"):
# Now reap all defunct children. # Now reap all defunct children.
for pid in self.active_children.copy(): for pid in self.active_children.copy():
try: 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 # if the child hasn't exited yet, pid will be 0 and ignored by
# discard() below # discard() below
self.active_children.discard(pid) self.active_children.discard(pid)
...@@ -620,6 +621,10 @@ if hasattr(os, "fork"): ...@@ -620,6 +621,10 @@ if hasattr(os, "fork"):
finally: finally:
os._exit(status) os._exit(status)
def server_close(self):
super().server_close()
self.collect_children(blocking=True)
class ThreadingMixIn: class ThreadingMixIn:
"""Mix-in class to handle each request in a new thread.""" """Mix-in class to handle each request in a new thread."""
......
...@@ -144,6 +144,10 @@ class SocketServerTest(unittest.TestCase): ...@@ -144,6 +144,10 @@ class SocketServerTest(unittest.TestCase):
t.join() t.join()
server.server_close() server.server_close()
self.assertEqual(-1, server.socket.fileno()) 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") if verbose: print("done")
def stream_examine(self, proto, addr): def stream_examine(self, proto, addr):
...@@ -371,10 +375,7 @@ class ThreadingErrorTestServer(socketserver.ThreadingMixIn, ...@@ -371,10 +375,7 @@ class ThreadingErrorTestServer(socketserver.ThreadingMixIn,
if HAVE_FORKING: if HAVE_FORKING:
class ForkingErrorTestServer(socketserver.ForkingMixIn, BaseErrorTestServer): class ForkingErrorTestServer(socketserver.ForkingMixIn, BaseErrorTestServer):
def wait_done(self): pass
[child] = self.active_children
os.waitpid(child, 0)
self.active_children.clear()
class SocketWriterTest(unittest.TestCase): class SocketWriterTest(unittest.TestCase):
......
socketserver.ForkingMixIn.server_close() now waits until all child processes
completed to prevent leaking zombie processes.
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