Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions Lib/socketserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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."""
Expand Down
9 changes: 5 additions & 4 deletions Lib/test/test_socketserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
socketserver.ForkingMixIn.server_close() now waits until all child processes
completed to prevent leaking zombie processes.