mirror of
https://github.com/python/cpython.git
synced 2025-08-01 15:43:13 +00:00
Issue #11657: Fix sending file descriptors over 255 over a multiprocessing Pipe.
Also added some tests.
This commit is contained in:
parent
cdaafe0f9e
commit
a1a8da8bf5
3 changed files with 87 additions and 3 deletions
|
@ -15,6 +15,7 @@ import array
|
|||
import socket
|
||||
import random
|
||||
import logging
|
||||
import errno
|
||||
from test import test_support
|
||||
from StringIO import StringIO
|
||||
_multiprocessing = test_support.import_module('_multiprocessing')
|
||||
|
@ -32,7 +33,7 @@ import multiprocessing.managers
|
|||
import multiprocessing.heap
|
||||
import multiprocessing.pool
|
||||
|
||||
from multiprocessing import util
|
||||
from multiprocessing import util, reduction
|
||||
|
||||
try:
|
||||
from multiprocessing.sharedctypes import Value, copy
|
||||
|
@ -40,6 +41,11 @@ try:
|
|||
except ImportError:
|
||||
HAS_SHAREDCTYPES = False
|
||||
|
||||
try:
|
||||
import msvcrt
|
||||
except ImportError:
|
||||
msvcrt = None
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
@ -68,6 +74,11 @@ HAVE_GETVALUE = not getattr(_multiprocessing,
|
|||
|
||||
WIN32 = (sys.platform == "win32")
|
||||
|
||||
try:
|
||||
MAXFD = os.sysconf("SC_OPEN_MAX")
|
||||
except:
|
||||
MAXFD = 256
|
||||
|
||||
#
|
||||
# Some tests require ctypes
|
||||
#
|
||||
|
@ -1481,6 +1492,76 @@ class _TestConnection(BaseTestCase):
|
|||
|
||||
self.assertRaises(ValueError, a.send_bytes, msg, 4, -1)
|
||||
|
||||
@classmethod
|
||||
def _is_fd_assigned(cls, fd):
|
||||
try:
|
||||
os.fstat(fd)
|
||||
except OSError as e:
|
||||
if e.errno == errno.EBADF:
|
||||
return False
|
||||
raise
|
||||
else:
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def _writefd(cls, conn, data, create_dummy_fds=False):
|
||||
if create_dummy_fds:
|
||||
for i in range(0, 256):
|
||||
if not cls._is_fd_assigned(i):
|
||||
os.dup2(conn.fileno(), i)
|
||||
fd = reduction.recv_handle(conn)
|
||||
if msvcrt:
|
||||
fd = msvcrt.open_osfhandle(fd, os.O_WRONLY)
|
||||
os.write(fd, data)
|
||||
os.close(fd)
|
||||
|
||||
def test_fd_transfer(self):
|
||||
if self.TYPE != 'processes':
|
||||
self.skipTest("only makes sense with processes")
|
||||
conn, child_conn = self.Pipe(duplex=True)
|
||||
|
||||
p = self.Process(target=self._writefd, args=(child_conn, b"foo"))
|
||||
p.start()
|
||||
with open(test_support.TESTFN, "wb") as f:
|
||||
fd = f.fileno()
|
||||
if msvcrt:
|
||||
fd = msvcrt.get_osfhandle(fd)
|
||||
reduction.send_handle(conn, fd, p.pid)
|
||||
p.join()
|
||||
with open(test_support.TESTFN, "rb") as f:
|
||||
self.assertEqual(f.read(), b"foo")
|
||||
|
||||
@unittest.skipIf(sys.platform == "win32",
|
||||
"test semantics don't make sense on Windows")
|
||||
@unittest.skipIf(MAXFD <= 256,
|
||||
"largest assignable fd number is too small")
|
||||
@unittest.skipUnless(hasattr(os, "dup2"),
|
||||
"test needs os.dup2()")
|
||||
def test_large_fd_transfer(self):
|
||||
# With fd > 256 (issue #11657)
|
||||
if self.TYPE != 'processes':
|
||||
self.skipTest("only makes sense with processes")
|
||||
conn, child_conn = self.Pipe(duplex=True)
|
||||
|
||||
p = self.Process(target=self._writefd, args=(child_conn, b"bar", True))
|
||||
p.start()
|
||||
with open(test_support.TESTFN, "wb") as f:
|
||||
fd = f.fileno()
|
||||
for newfd in range(256, MAXFD):
|
||||
if not self._is_fd_assigned(newfd):
|
||||
break
|
||||
else:
|
||||
self.fail("could not find an unassigned large file descriptor")
|
||||
os.dup2(fd, newfd)
|
||||
try:
|
||||
reduction.send_handle(conn, newfd, p.pid)
|
||||
finally:
|
||||
os.close(newfd)
|
||||
p.join()
|
||||
with open(test_support.TESTFN, "rb") as f:
|
||||
self.assertEqual(f.read(), b"bar")
|
||||
|
||||
|
||||
class _TestListenerClient(BaseTestCase):
|
||||
|
||||
ALLOWED_TYPES = ('processes', 'threads')
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue