Issue #21491: socketserver: Fix a race condition in child processes reaping.
This commit is contained in:
parent
ccc342d67a
commit
af4db37f2a
|
@ -539,35 +539,39 @@ class ForkingMixIn:
|
||||||
|
|
||||||
def collect_children(self):
|
def collect_children(self):
|
||||||
"""Internal routine to wait for children that have exited."""
|
"""Internal routine to wait for children that have exited."""
|
||||||
if self.active_children is None: return
|
if self.active_children is None:
|
||||||
while len(self.active_children) >= self.max_children:
|
return
|
||||||
# XXX: This will wait for any child process, not just ones
|
|
||||||
# spawned by this library. This could confuse other
|
|
||||||
# libraries that expect to be able to wait for their own
|
|
||||||
# children.
|
|
||||||
try:
|
|
||||||
pid, status = os.waitpid(0, 0)
|
|
||||||
except OSError:
|
|
||||||
pid = None
|
|
||||||
if pid not in self.active_children: continue
|
|
||||||
self.active_children.remove(pid)
|
|
||||||
|
|
||||||
# XXX: This loop runs more system calls than it ought
|
# If we're above the max number of children, wait and reap them until
|
||||||
# to. There should be a way to put the active_children into a
|
# we go back below threshold. Note that we use waitpid(-1) below to be
|
||||||
# process group and then use os.waitpid(-pgid) to wait for any
|
# able to collect children in size(<defunct children>) syscalls instead
|
||||||
# of that set, but I couldn't find a way to allocate pgids
|
# of size(<children>): the downside is that this might reap children
|
||||||
# that couldn't collide.
|
# which we didn't spawn, which is why we only resort to this when we're
|
||||||
for child in self.active_children:
|
# above max_children.
|
||||||
|
while len(self.active_children) >= self.max_children:
|
||||||
try:
|
try:
|
||||||
pid, status = os.waitpid(child, os.WNOHANG)
|
pid, _ = os.waitpid(-1, 0)
|
||||||
|
self.active_children.discard(pid)
|
||||||
|
except InterruptedError:
|
||||||
|
pass
|
||||||
|
except ChildProcessError:
|
||||||
|
# we don't have any children, we're done
|
||||||
|
self.active_children.clear()
|
||||||
except OSError:
|
except OSError:
|
||||||
pid = None
|
break
|
||||||
if not pid: continue
|
|
||||||
|
# Now reap all defunct children.
|
||||||
|
for pid in self.active_children.copy():
|
||||||
try:
|
try:
|
||||||
self.active_children.remove(pid)
|
pid, _ = os.waitpid(pid, os.WNOHANG)
|
||||||
except ValueError as e:
|
# if the child hasn't exited yet, pid will be 0 and ignored by
|
||||||
raise ValueError('%s. x=%d and list=%r' % (e.message, pid,
|
# discard() below
|
||||||
self.active_children))
|
self.active_children.discard(pid)
|
||||||
|
except ChildProcessError:
|
||||||
|
# someone else reaped it
|
||||||
|
self.active_children.discard(pid)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
def handle_timeout(self):
|
def handle_timeout(self):
|
||||||
"""Wait for zombies after self.timeout seconds of inactivity.
|
"""Wait for zombies after self.timeout seconds of inactivity.
|
||||||
|
@ -589,8 +593,8 @@ class ForkingMixIn:
|
||||||
if pid:
|
if pid:
|
||||||
# Parent process
|
# Parent process
|
||||||
if self.active_children is None:
|
if self.active_children is None:
|
||||||
self.active_children = []
|
self.active_children = set()
|
||||||
self.active_children.append(pid)
|
self.active_children.add(pid)
|
||||||
self.close_request(request)
|
self.close_request(request)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -103,6 +103,8 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #21491: socketserver: Fix a race condition in child processes reaping.
|
||||||
|
|
||||||
- Issue #21719: Added the ``st_file_attributes`` field to os.stat_result on
|
- Issue #21719: Added the ``st_file_attributes`` field to os.stat_result on
|
||||||
Windows.
|
Windows.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue