|
35 | 35 | import multiprocessing.heap |
36 | 36 | import multiprocessing.pool |
37 | 37 |
|
38 | | -from multiprocessing import util |
| 38 | +from multiprocessing import util, reduction |
39 | 39 |
|
40 | 40 | try: |
41 | 41 | from multiprocessing.sharedctypes import Value, copy |
42 | 42 | HAS_SHAREDCTYPES = True |
43 | 43 | except ImportError: |
44 | 44 | HAS_SHAREDCTYPES = False |
45 | 45 |
|
| 46 | +try: |
| 47 | + import msvcrt |
| 48 | +except ImportError: |
| 49 | + msvcrt = None |
| 50 | + |
46 | 51 | # |
47 | 52 | # |
48 | 53 | # |
@@ -72,6 +77,11 @@ def latin(s): |
72 | 77 |
|
73 | 78 | WIN32 = (sys.platform == "win32") |
74 | 79 |
|
| 80 | +try: |
| 81 | + MAXFD = os.sysconf("SC_OPEN_MAX") |
| 82 | +except: |
| 83 | + MAXFD = 256 |
| 84 | + |
75 | 85 | # |
76 | 86 | # Some tests require ctypes |
77 | 87 | # |
@@ -1538,6 +1548,76 @@ def test_sendbytes(self): |
1538 | 1548 |
|
1539 | 1549 | self.assertRaises(ValueError, a.send_bytes, msg, 4, -1) |
1540 | 1550 |
|
| 1551 | + @classmethod |
| 1552 | + def _is_fd_assigned(cls, fd): |
| 1553 | + try: |
| 1554 | + os.fstat(fd) |
| 1555 | + except OSError as e: |
| 1556 | + if e.errno == errno.EBADF: |
| 1557 | + return False |
| 1558 | + raise |
| 1559 | + else: |
| 1560 | + return True |
| 1561 | + |
| 1562 | + @classmethod |
| 1563 | + def _writefd(cls, conn, data, create_dummy_fds=False): |
| 1564 | + if create_dummy_fds: |
| 1565 | + for i in range(0, 256): |
| 1566 | + if not cls._is_fd_assigned(i): |
| 1567 | + os.dup2(conn.fileno(), i) |
| 1568 | + fd = reduction.recv_handle(conn) |
| 1569 | + if msvcrt: |
| 1570 | + fd = msvcrt.open_osfhandle(fd, os.O_WRONLY) |
| 1571 | + os.write(fd, data) |
| 1572 | + os.close(fd) |
| 1573 | + |
| 1574 | + def test_fd_transfer(self): |
| 1575 | + if self.TYPE != 'processes': |
| 1576 | + self.skipTest("only makes sense with processes") |
| 1577 | + conn, child_conn = self.Pipe(duplex=True) |
| 1578 | + |
| 1579 | + p = self.Process(target=self._writefd, args=(child_conn, b"foo")) |
| 1580 | + p.start() |
| 1581 | + with open(test.support.TESTFN, "wb") as f: |
| 1582 | + fd = f.fileno() |
| 1583 | + if msvcrt: |
| 1584 | + fd = msvcrt.get_osfhandle(fd) |
| 1585 | + reduction.send_handle(conn, fd, p.pid) |
| 1586 | + p.join() |
| 1587 | + with open(test.support.TESTFN, "rb") as f: |
| 1588 | + self.assertEqual(f.read(), b"foo") |
| 1589 | + |
| 1590 | + @unittest.skipIf(sys.platform == "win32", |
| 1591 | + "test semantics don't make sense on Windows") |
| 1592 | + @unittest.skipIf(MAXFD <= 256, |
| 1593 | + "largest assignable fd number is too small") |
| 1594 | + @unittest.skipUnless(hasattr(os, "dup2"), |
| 1595 | + "test needs os.dup2()") |
| 1596 | + def test_large_fd_transfer(self): |
| 1597 | + # With fd > 256 (issue #11657) |
| 1598 | + if self.TYPE != 'processes': |
| 1599 | + self.skipTest("only makes sense with processes") |
| 1600 | + conn, child_conn = self.Pipe(duplex=True) |
| 1601 | + |
| 1602 | + p = self.Process(target=self._writefd, args=(child_conn, b"bar", True)) |
| 1603 | + p.start() |
| 1604 | + with open(test.support.TESTFN, "wb") as f: |
| 1605 | + fd = f.fileno() |
| 1606 | + for newfd in range(256, MAXFD): |
| 1607 | + if not self._is_fd_assigned(newfd): |
| 1608 | + break |
| 1609 | + else: |
| 1610 | + self.fail("could not find an unassigned large file descriptor") |
| 1611 | + os.dup2(fd, newfd) |
| 1612 | + try: |
| 1613 | + reduction.send_handle(conn, newfd, p.pid) |
| 1614 | + finally: |
| 1615 | + os.close(newfd) |
| 1616 | + p.join() |
| 1617 | + with open(test.support.TESTFN, "rb") as f: |
| 1618 | + self.assertEqual(f.read(), b"bar") |
| 1619 | + |
| 1620 | + |
1541 | 1621 | class _TestListenerClient(BaseTestCase): |
1542 | 1622 |
|
1543 | 1623 | ALLOWED_TYPES = ('processes', 'threads') |
|
0 commit comments