mirror of
https://github.com/python/cpython.git
synced 2025-08-01 07:33:08 +00:00
Prevent SocketServer.ForkingMixIn from waiting on child processes that it
didn't create, in most cases. When there are max_children handlers running, it will still wait for any child process, not just handler processes.
This commit is contained in:
parent
a6298528e1
commit
392c159ad6
2 changed files with 47 additions and 19 deletions
|
@ -440,18 +440,30 @@ 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."""
|
||||||
while self.active_children:
|
if self.active_children is None: return
|
||||||
if len(self.active_children) < self.max_children:
|
while len(self.active_children) >= self.max_children:
|
||||||
options = os.WNOHANG
|
# XXX: This will wait for any child process, not just ones
|
||||||
else:
|
# spawned by this library. This could confuse other
|
||||||
# If the maximum number of children are already
|
# libraries that expect to be able to wait for their own
|
||||||
# running, block while waiting for a child to exit
|
# children.
|
||||||
options = 0
|
|
||||||
try:
|
try:
|
||||||
pid, status = os.waitpid(0, options)
|
pid, status = os.waitpid(0, options=0)
|
||||||
except os.error:
|
except os.error:
|
||||||
pid = None
|
pid = None
|
||||||
if not pid: break
|
if pid not in self.active_children: continue
|
||||||
|
self.active_children.remove(pid)
|
||||||
|
|
||||||
|
# XXX: This loop runs more system calls than it ought
|
||||||
|
# to. There should be a way to put the active_children into a
|
||||||
|
# process group and then use os.waitpid(-pgid) to wait for any
|
||||||
|
# of that set, but I couldn't find a way to allocate pgids
|
||||||
|
# that couldn't collide.
|
||||||
|
for child in self.active_children:
|
||||||
|
try:
|
||||||
|
pid, status = os.waitpid(child, os.WNOHANG)
|
||||||
|
except os.error:
|
||||||
|
pid = None
|
||||||
|
if not pid: continue
|
||||||
try:
|
try:
|
||||||
self.active_children.remove(pid)
|
self.active_children.remove(pid)
|
||||||
except ValueError, e:
|
except ValueError, e:
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
Test suite for SocketServer.py.
|
Test suite for SocketServer.py.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import contextlib
|
||||||
import errno
|
import errno
|
||||||
import imp
|
import imp
|
||||||
import os
|
import os
|
||||||
|
@ -82,6 +83,18 @@ class ServerThread(threading.Thread):
|
||||||
if verbose: print "thread: done"
|
if verbose: print "thread: done"
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def simple_subprocess(testcase):
|
||||||
|
pid = os.fork()
|
||||||
|
if pid == 0:
|
||||||
|
# Don't throw an exception; it would be caught by the test harness.
|
||||||
|
os._exit(72)
|
||||||
|
yield None
|
||||||
|
pid2, status = os.waitpid(pid, 0)
|
||||||
|
testcase.assertEquals(pid2, pid)
|
||||||
|
testcase.assertEquals(72 << 8, status)
|
||||||
|
|
||||||
|
|
||||||
class SocketServerTest(unittest.TestCase):
|
class SocketServerTest(unittest.TestCase):
|
||||||
"""Test all socket servers."""
|
"""Test all socket servers."""
|
||||||
|
|
||||||
|
@ -183,10 +196,11 @@ class SocketServerTest(unittest.TestCase):
|
||||||
self.stream_examine)
|
self.stream_examine)
|
||||||
|
|
||||||
if HAVE_FORKING:
|
if HAVE_FORKING:
|
||||||
def test_ThreadingTCPServer(self):
|
def test_ForkingTCPServer(self):
|
||||||
self.run_server(SocketServer.ForkingTCPServer,
|
with simple_subprocess(self):
|
||||||
SocketServer.StreamRequestHandler,
|
self.run_server(SocketServer.ForkingTCPServer,
|
||||||
self.stream_examine)
|
SocketServer.StreamRequestHandler,
|
||||||
|
self.stream_examine)
|
||||||
|
|
||||||
if HAVE_UNIX_SOCKETS:
|
if HAVE_UNIX_SOCKETS:
|
||||||
def test_UnixStreamServer(self):
|
def test_UnixStreamServer(self):
|
||||||
|
@ -201,9 +215,10 @@ class SocketServerTest(unittest.TestCase):
|
||||||
|
|
||||||
if HAVE_FORKING:
|
if HAVE_FORKING:
|
||||||
def test_ForkingUnixStreamServer(self):
|
def test_ForkingUnixStreamServer(self):
|
||||||
self.run_server(ForkingUnixStreamServer,
|
with simple_subprocess(self):
|
||||||
SocketServer.StreamRequestHandler,
|
self.run_server(ForkingUnixStreamServer,
|
||||||
self.stream_examine)
|
SocketServer.StreamRequestHandler,
|
||||||
|
self.stream_examine)
|
||||||
|
|
||||||
def test_UDPServer(self):
|
def test_UDPServer(self):
|
||||||
self.run_server(SocketServer.UDPServer,
|
self.run_server(SocketServer.UDPServer,
|
||||||
|
@ -217,9 +232,10 @@ class SocketServerTest(unittest.TestCase):
|
||||||
|
|
||||||
if HAVE_FORKING:
|
if HAVE_FORKING:
|
||||||
def test_ForkingUDPServer(self):
|
def test_ForkingUDPServer(self):
|
||||||
self.run_server(SocketServer.ForkingUDPServer,
|
with simple_subprocess(self):
|
||||||
SocketServer.DatagramRequestHandler,
|
self.run_server(SocketServer.ForkingUDPServer,
|
||||||
self.dgram_examine)
|
SocketServer.DatagramRequestHandler,
|
||||||
|
self.dgram_examine)
|
||||||
|
|
||||||
# Alas, on Linux (at least) recvfrom() doesn't return a meaningful
|
# Alas, on Linux (at least) recvfrom() doesn't return a meaningful
|
||||||
# client address so this cannot work:
|
# client address so this cannot work:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue