| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent eea1a49 commit a7f3e56
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1448,7 +1448,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo | |||
| 1448 | 1448 | Return a pair of file descriptors ``(r, w)`` usable for reading and writing, | |
| 1449 | 1449 | respectively. | |
| 1450 | 1450 | ||
| 1451 | - .. availability:: Unix, not WASI, not macOS, not iOS. | ||
| 1451 | + .. availability:: Unix, macOS >= 27.0, not WASI, not iOS. | ||
| 1452 | 1452 | ||
| 1453 | 1453 | .. versionadded:: 3.3 | |
| 1454 | 1454 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2350,6 +2350,22 @@ def test_pwritev(self): | |||
| 2350 | 2350 | self.assertNotHasAttr(os, "pwritev") | |
| 2351 | 2351 | self.assertNotHasAttr(os, "preadv") | |
| 2352 | 2352 | ||
| 2353 | + def test_pipe2(self): | ||
| 2354 | + self._verify_available("HAVE_PIPE2") | ||
| 2355 | + if self.mac_ver >= (27, 0): | ||
| 2356 | + self.assertHasAttr(os, "pipe2") | ||
| 2357 | + else: | ||
| 2358 | + self.assertNotHasAttr(os, "pipe2") | ||
| 2359 | + | ||
| 2360 | + def test_dup3(self): | ||
| 2361 | + self._verify_available("HAVE_DUP3") | ||
| 2362 | + r, w = os.pipe() | ||
| 2363 | + self.addCleanup(os.close, r) | ||
| 2364 | + self.addCleanup(os.close, w) | ||
| 2365 | + # Must not crash even when dup3 unavailable at runtime. | ||
| 2366 | + # os.dup2 returns fd2 (here w); do not double-close. | ||
| 2367 | + os.dup2(r, w, inheritable=False) | ||
| 2368 | + | ||
| 2353 | 2369 | def test_stat(self): | |
| 2354 | 2370 | self._verify_available("HAVE_FSTATAT") | |
| 2355 | 2371 | if self.mac_ver >= (10, 10): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,4 @@ | |||
| 1 | + On macOS, add run-time checks around the syscalls :manpage:`pipe2 (2)` and | ||
| 2 | + :manpage:`dup3 (2)`, in addition to the existing build-time checks. This | ||
| 3 | + means that Python built on macOS 27 (where these calls are available) can | ||
| 4 | + run on macOS 26 (where they aren't). | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -504,6 +504,8 @@ static const unsigned int _Py_STATX_KNOWN = (STATX_BASIC_STATS | STATX_BTIME | |||
| 504 | 504 | # define HAVE_MKFIFOAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) | |
| 505 | 505 | # define HAVE_MKNODAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) | |
| 506 | 506 | # define HAVE_PTSNAME_R_RUNTIME __builtin_available(macOS 10.13.4, iOS 11.3, tvOS 11.3, watchOS 4.3, *) | |
| 507 | + # define HAVE_DUP3_RUNTIME __builtin_available(macOS 27.0, *) | ||
| 508 | + # define HAVE_PIPE2_RUNTIME __builtin_available(macOS 27.0, *) | ||
| 507 | 509 | ||
| 508 | 510 | # define HAVE_POSIX_SPAWN_SETSID_RUNTIME __builtin_available(macOS 10.15, *) | |
| 509 | 511 | ||
@@ -589,6 +591,14 @@ static const unsigned int _Py_STATX_KNOWN = (STATX_BASIC_STATS | STATX_BTIME | |||
| 589 | 591 | # define HAVE_PTSNAME_R_RUNTIME (ptsname_r != NULL) | |
| 590 | 592 | # endif | |
| 591 | 593 | ||
| 594 | + # ifdef HAVE_DUP3 | ||
| 595 | + # define HAVE_DUP3_RUNTIME (dup3 != NULL) | ||
| 596 | + # endif | ||
| 597 | + | ||
| 598 | + # ifdef HAVE_PIPE2 | ||
| 599 | + # define HAVE_PIPE2_RUNTIME (pipe2 != NULL) | ||
| 600 | + # endif | ||
| 601 | + | ||
| 592 | 602 | #endif | |
| 593 | 603 | ||
| 594 | 604 | #ifdef HAVE_FUTIMESAT | |
@@ -619,6 +629,8 @@ static const unsigned int _Py_STATX_KNOWN = (STATX_BASIC_STATS | STATX_BTIME | |||
| 619 | 629 | # define HAVE_MKFIFOAT_RUNTIME 1 | |
| 620 | 630 | # define HAVE_MKNODAT_RUNTIME 1 | |
| 621 | 631 | # define HAVE_PTSNAME_R_RUNTIME 1 | |
| 632 | + # define HAVE_DUP3_RUNTIME 1 | ||
| 633 | + # define HAVE_PIPE2_RUNTIME 1 | ||
| 622 | 634 | #endif | |
| 623 | 635 | ||
| 624 | 636 | ||
@@ -11833,11 +11845,16 @@ os_dup2_impl(PyObject *module, int fd, int fd2, int inheritable) | |||
| 11833 | 11845 | /*[clinic end generated code: output=bc059d34a73404d1 input=c3cddda8922b038d]*/ | |
| 11834 | 11846 | { | |
| 11835 | 11847 | int res = 0; | |
| 11836 | - #if defined(HAVE_DUP3) && \ | ||
| 11837 | - !(defined(HAVE_FCNTL_H) && defined(F_DUP2FD_CLOEXEC)) | ||
| 11838 | - /* dup3() is available on Linux 2.6.27+ and glibc 2.9 */ | ||
| 11839 | - static int dup3_works = -1; | ||
| 11840 | - #endif | ||
| 11848 | + | ||
| 11849 | + /* dup3() is available on Linux 2.6.27+ and glibc 2.9 and macOS 27.0; | ||
| 11850 | + * it needs runtime detection for the case of running on older kernels. | ||
| 11851 | + * Values: -1: unknown; 0: doesn't work; 1: works | ||
| 11852 | + * For thread safety, use a process-global with one read & one store, | ||
| 11853 | + * both relaxed. (It's fine if two threads race and do the detection | ||
| 11854 | + * simultaneously; they should get the same result.) | ||
| 11855 | + */ | ||
| 11856 | + static int dup3_works_atomic = -1; | ||
| 11857 | + (void) dup3_works_atomic; // unused on some platforms | ||
| 11841 | 11858 | ||
| 11842 | 11859 | /* dup2() can fail with EINTR if the target FD is already open, because it | |
| 11843 | 11860 | * then has to be closed. See os_close_impl() for why we don't handle EINTR | |
@@ -11876,18 +11893,27 @@ os_dup2_impl(PyObject *module, int fd, int fd2, int inheritable) | |||
| 11876 | 11893 | #else | |
| 11877 | 11894 | ||
| 11878 | 11895 | #ifdef HAVE_DUP3 | |
| 11896 | + int dup3_works = FT_ATOMIC_LOAD_INT_RELAXED(dup3_works_atomic); | ||
| 11879 | 11897 | if (!inheritable && dup3_works != 0) { | |
| 11880 | - Py_BEGIN_ALLOW_THREADS | ||
| 11881 | - res = dup3(fd, fd2, O_CLOEXEC); | ||
| 11882 | - Py_END_ALLOW_THREADS | ||
| 11883 | - if (res < 0) { | ||
| 11884 | - if (dup3_works == -1) | ||
| 11885 | - dup3_works = (errno != ENOSYS); | ||
| 11886 | - if (dup3_works) { | ||
| 11887 | - posix_error(); | ||
| 11888 | - return -1; | ||
| 11898 | + if (HAVE_DUP3_RUNTIME) { | ||
| 11899 | + Py_BEGIN_ALLOW_THREADS | ||
| 11900 | + res = dup3(fd, fd2, O_CLOEXEC); | ||
| 11901 | + Py_END_ALLOW_THREADS | ||
| 11902 | + if (res < 0) { | ||
| 11903 | + if (dup3_works == -1) { | ||
| 11904 | + dup3_works = (errno != ENOSYS); | ||
| 11905 | + FT_ATOMIC_STORE_INT_RELAXED(dup3_works_atomic, dup3_works); | ||
| 11906 | + } | ||
| 11907 | + if (dup3_works) { | ||
| 11908 | + posix_error(); | ||
| 11909 | + return -1; | ||
| 11910 | + } | ||
| 11889 | 11911 | } | |
| 11890 | 11912 | } | |
| 11913 | + else { | ||
| 11914 | + dup3_works = 0; | ||
| 11915 | + FT_ATOMIC_STORE_INT_RELAXED(dup3_works_atomic, dup3_works); | ||
| 11916 | + } | ||
| 11891 | 11917 | } | |
| 11892 | 11918 | ||
| 11893 | 11919 | if (inheritable || dup3_works == 0) | |
@@ -12728,7 +12754,13 @@ os_pipe_impl(PyObject *module) | |||
| 12728 | 12754 | SECURITY_ATTRIBUTES attr; | |
| 12729 | 12755 | BOOL ok; | |
| 12730 | 12756 | #else | |
| 12731 | - int res; | ||
| 12757 | + int res = -1; | ||
| 12758 | + | ||
| 12759 | + /* pipe2() is available on some newer linux/glibc & macOS; | ||
| 12760 | + * use the same runtime detection as for dup3 above. | ||
| 12761 | + */ | ||
| 12762 | + static int pipe2_works_atomic = -1; | ||
| 12763 | + (void) pipe2_works_atomic; // unused on some platforms | ||
| 12732 | 12764 | #endif | |
| 12733 | 12765 | ||
| 12734 | 12766 | #ifdef MS_WINDOWS | |
@@ -12754,11 +12786,30 @@ os_pipe_impl(PyObject *module) | |||
| 12754 | 12786 | #else | |
| 12755 | 12787 | ||
| 12756 | 12788 | #ifdef HAVE_PIPE2 | |
| 12757 | - Py_BEGIN_ALLOW_THREADS | ||
| 12758 | - res = pipe2(fds, O_CLOEXEC); | ||
| 12759 | - Py_END_ALLOW_THREADS | ||
| 12789 | + int pipe2_works = FT_ATOMIC_LOAD_INT_RELAXED(pipe2_works_atomic); | ||
| 12790 | + if (pipe2_works != 0) { | ||
| 12791 | + if (HAVE_PIPE2_RUNTIME) { | ||
| 12792 | + Py_BEGIN_ALLOW_THREADS | ||
| 12793 | + res = pipe2(fds, O_CLOEXEC); | ||
| 12794 | + Py_END_ALLOW_THREADS | ||
| 12795 | + if (pipe2_works == -1) { | ||
| 12796 | + if (res != 0 && errno == ENOSYS) { | ||
| 12797 | + pipe2_works = 0; | ||
| 12798 | + } | ||
| 12799 | + else { | ||
| 12800 | + // pipe2 is present but this call failed | ||
| 12801 | + pipe2_works = 1; | ||
| 12802 | + } | ||
| 12803 | + FT_ATOMIC_STORE_INT_RELAXED(pipe2_works_atomic, pipe2_works); | ||
| 12804 | + } | ||
| 12805 | + } | ||
| 12806 | + else { | ||
| 12807 | + pipe2_works = 0; | ||
| 12808 | + FT_ATOMIC_STORE_INT_RELAXED(pipe2_works_atomic, pipe2_works); | ||
| 12809 | + } | ||
| 12810 | + } | ||
| 12760 | 12811 | ||
| 12761 | - if (res != 0 && errno == ENOSYS) | ||
| 12812 | + if (pipe2_works == 0) | ||
| 12762 | 12813 | { | |
| 12763 | 12814 | #endif | |
| 12764 | 12815 | Py_BEGIN_ALLOW_THREADS | |
@@ -12781,8 +12832,9 @@ os_pipe_impl(PyObject *module) | |||
| 12781 | 12832 | } | |
| 12782 | 12833 | #endif | |
| 12783 | 12834 | ||
| 12784 | - if (res != 0) | ||
| 12835 | + if (res != 0) { | ||
| 12785 | 12836 | return PyErr_SetFromErrno(PyExc_OSError); | |
| 12837 | + } | ||
| 12786 | 12838 | #endif /* !MS_WINDOWS */ | |
| 12787 | 12839 | return Py_BuildValue("(ii)", fds[0], fds[1]); | |
| 12788 | 12840 | } | |
@@ -12812,9 +12864,17 @@ os_pipe2_impl(PyObject *module, int flags) | |||
| 12812 | 12864 | int fds[2]; | |
| 12813 | 12865 | int res; | |
| 12814 | 12866 | ||
| 12815 | - res = pipe2(fds, flags); | ||
| 12816 | - if (res != 0) | ||
| 12867 | + if (HAVE_PIPE2_RUNTIME) { | ||
| 12868 | + res = pipe2(fds, flags); | ||
| 12869 | + } | ||
| 12870 | + else { | ||
| 12871 | + res = -1; | ||
| 12872 | + errno = ENOSYS; | ||
| 12873 | + } | ||
| 12874 | + if (res != 0) { | ||
| 12817 | 12875 | return posix_error(); | |
| 12876 | + } | ||
| 12877 | + | ||
| 12818 | 12878 | return Py_BuildValue("(ii)", fds[0], fds[1]); | |
| 12819 | 12879 | } | |
| 12820 | 12880 | #endif /* HAVE_PIPE2 */ | |
@@ -18803,6 +18863,22 @@ posixmodule_exec(PyObject *m) | |||
| 18803 | 18863 | } | |
| 18804 | 18864 | #endif | |
| 18805 | 18865 | ||
| 18866 | + #if HAVE_PIPE2 | ||
| 18867 | + if (HAVE_PIPE2_RUNTIME) { | ||
| 18868 | + // Do nothing. (`__builtin_available` doesn't allow `!`; see | ||
| 18869 | + // "using negations" in a comment above.) | ||
| 18870 | + } | ||
| 18871 | + else { | ||
| 18872 | + PyObject* dct = PyModule_GetDict(m); | ||
| 18873 | + if (dct == NULL) { | ||
| 18874 | + return -1; | ||
| 18875 | + } | ||
| 18876 | + if (PyDict_PopString(dct, "pipe2", NULL) < 0) { | ||
| 18877 | + return -1; | ||
| 18878 | + } | ||
| 18879 | + } | ||
| 18880 | + #endif | ||
| 18881 | + | ||
| 18806 | 18882 | /* Initialize environ dictionary */ | |
| 18807 | 18883 | if (PyModule_Add(m, "environ", convertenviron()) != 0) { | |
| 18808 | 18884 | return -1; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -19,9 +19,9 @@ Python/bootstrap_hash.c py_getrandom getrandom_works - | |||
| 19 | 19 | Python/bootstrap_hash.c py_getentropy getentropy_works - | |
| 20 | 20 | Python/fileutils.c - _Py_open_cloexec_works - | |
| 21 | 21 | Python/fileutils.c set_inheritable ioctl_works - | |
| 22 | - # (set lazily, *after* first init) | ||
| 23 | - # XXX Is this thread-safe? | ||
| 24 | - Modules/posixmodule.c os_dup2_impl dup3_works - | ||
| 22 | + # (set lazily, atomically, *after* first init) | ||
| 23 | + Modules/posixmodule.c os_dup2_impl dup3_works_atomic - | ||
| 24 | + Modules/posixmodule.c os_pipe_impl pipe2_works_atomic - | ||
| 25 | 25 | ||
| 26 | 26 | ## guards around resource init | |
| 27 | 27 | Python/thread_pthread.h PyThread__init_thread lib_initialized - | |
| Back | FazBrowse Home | New Git URL |
0 commit comments