Fix closes issue #11109 -  socketserver.ForkingMixIn leaves zombies, also fails to reap all zombies in one pass.

A new method called service_action is made available in BaseServer, called by
serve_forever loop. This useful in cases where Mixins can use it for cleanup
action. ForkingMixin class uses service_action to collect the zombie child
processes. Initial Patch by Justin Wark.
diff --git a/Lib/socketserver.py b/Lib/socketserver.py
index 7389608..a2f0f39 100644
--- a/Lib/socketserver.py
+++ b/Lib/socketserver.py
@@ -82,7 +82,7 @@
 data is stored externally (e.g. in the file system), a synchronous
 class will essentially render the service "deaf" while one request is
 being handled -- which may be for a very long time if a client is slow
-to reqd all the data it has requested.  Here a threading or forking
+to recv all the data it has requested.  Here a threading or forking
 server is appropriate.
 
 In some cases, it may be appropriate to process part of a request
@@ -170,6 +170,7 @@
     - process_request(request, client_address)
     - shutdown_request(request)
     - close_request(request)
+    - service_actions()
     - handle_error()
 
     Methods for derived classes:
@@ -225,6 +226,8 @@
                 r, w, e = select.select([self], [], [], poll_interval)
                 if self in r:
                     self._handle_request_noblock()
+
+                self.service_actions()
         finally:
             self.__shutdown_request = False
             self.__is_shut_down.set()
@@ -239,6 +242,14 @@
         self.__shutdown_request = True
         self.__is_shut_down.wait()
 
+    def service_actions(self):
+        """Called by the serve_forever() loop.
+
+        May be overridden by a subclass / Mixin to implement any code that
+        needs to be run during the loop.
+        """
+        pass
+
     # The distinction between handling, getting, processing and
     # finishing a request is fairly arbitrary.  Remember:
     #
@@ -539,9 +550,15 @@
         """
         self.collect_children()
 
+    def service_actions(self):
+        """Collect the zombie child processes regularly in the ForkingMixin.
+
+        service_actions is called in the BaseServer's serve_forver loop.
+        """
+        self.collect_children()
+
     def process_request(self, request, client_address):
         """Fork a new subprocess to process the request."""
-        self.collect_children()
         pid = os.fork()
         if pid:
             # Parent process
@@ -549,6 +566,7 @@
                 self.active_children = []
             self.active_children.append(pid)
             self.close_request(request)
+            return
         else:
             # Child process.
             # This must never return, hence os._exit()!