@@ -523,35 +523,39 @@ class ForkingMixIn:
523523
524524 def collect_children (self ):
525525 """Internal routine to wait for children that have exited."""
526- if self .active_children is None : return
526+ if self .active_children is None :
527+ return
528+
529+ # If we're above the max number of children, wait and reap them until
530+ # we go back below threshold. Note that we use waitpid(-1) below to be
531+ # able to collect children in size(<defunct children>) syscalls instead
532+ # of size(<children>): the downside is that this might reap children
533+ # which we didn't spawn, which is why we only resort to this when we're
534+ # above max_children.
527535 while len (self .active_children ) >= self .max_children :
528- # XXX: This will wait for any child process, not just ones
529- # spawned by this library. This could confuse other
530- # libraries that expect to be able to wait for their own
531- # children.
532536 try :
533- pid , status = os .waitpid (0 , 0 )
537+ pid , _ = os .waitpid (- 1 , 0 )
538+ self .active_children .discard (pid )
539+ except InterruptedError :
540+ pass
541+ except ChildProcessError :
542+ # we don't have any children, we're done
543+ self .active_children .clear ()
534544 except OSError :
535- pid = None
536- if pid not in self .active_children : continue
537- self .active_children .remove (pid )
538-
539- # XXX: This loop runs more system calls than it ought
540- # to. There should be a way to put the active_children into a
541- # process group and then use os.waitpid(-pgid) to wait for any
542- # of that set, but I couldn't find a way to allocate pgids
543- # that couldn't collide.
544- for child in self .active_children :
545+ break
546+
547+ # Now reap all defunct children.
548+ for pid in self .active_children .copy ():
545549 try :
546- pid , status = os .waitpid (child , os .WNOHANG )
550+ pid , _ = os .waitpid (pid , os .WNOHANG )
551+ # if the child hasn't exited yet, pid will be 0 and ignored by
552+ # discard() below
553+ self .active_children .discard (pid )
554+ except ChildProcessError :
555+ # someone else reaped it
556+ self .active_children .discard (pid )
547557 except OSError :
548- pid = None
549- if not pid : continue
550- try :
551- self .active_children .remove (pid )
552- except ValueError as e :
553- raise ValueError ('%s. x=%d and list=%r' % (e .message , pid ,
554- self .active_children ))
558+ pass
555559
556560 def handle_timeout (self ):
557561 """Wait for zombies after self.timeout seconds of inactivity.
@@ -573,8 +577,8 @@ def process_request(self, request, client_address):
573577 if pid :
574578 # Parent process
575579 if self .active_children is None :
576- self .active_children = []
577- self .active_children .append (pid )
580+ self .active_children = set ()
581+ self .active_children .add (pid )
578582 self .close_request (request )
579583 return
580584 else :
0 commit comments