@@ -504,6 +504,8 @@ static const unsigned int _Py_STATX_KNOWN = (STATX_BASIC_STATS | STATX_BTIME
504504# define HAVE_MKFIFOAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
505505# define HAVE_MKNODAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
506506# 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, *)
507509
508510# define HAVE_POSIX_SPAWN_SETSID_RUNTIME __builtin_available(macOS 10.15, *)
509511
@@ -589,6 +591,14 @@ static const unsigned int _Py_STATX_KNOWN = (STATX_BASIC_STATS | STATX_BTIME
589591# define HAVE_PTSNAME_R_RUNTIME (ptsname_r != NULL)
590592# endif
591593
594+ # ifdef HAVE_DUP3_RUNTIME
595+ # define HAVE_DUP3_RUNTIME (dup3 != NULL)
596+ # endif
597+
598+ # ifdef HAVE_PIPE2_RUNTIME
599+ # define HAVE_PIPE2_RUNTIME (pipe2 != NULL)
600+ # endif
601+
592602#endif
593603
594604#ifdef HAVE_FUTIMESAT
@@ -619,6 +629,8 @@ static const unsigned int _Py_STATX_KNOWN = (STATX_BASIC_STATS | STATX_BTIME
619629# define HAVE_MKFIFOAT_RUNTIME 1
620630# define HAVE_MKNODAT_RUNTIME 1
621631# define HAVE_PTSNAME_R_RUNTIME 1
632+ # define HAVE_DUP3_RUNTIME 1
633+ # define HAVE_PIPE2_RUNTIME 1
622634#endif
623635
624636
@@ -11866,11 +11878,13 @@ os_dup2_impl(PyObject *module, int fd, int fd2, int inheritable)
1186611878/*[clinic end generated code: output=bc059d34a73404d1 input=c3cddda8922b038d]*/
1186711879{
1186811880 int res = 0;
11869- #if defined(HAVE_DUP3) && \
11870- !(defined(HAVE_FCNTL_H) && defined(F_DUP2FD_CLOEXEC))
11871- /* dup3() is available on Linux 2.6.27+ and glibc 2.9 */
11881+
11882+ /* dup3() is available on Linux 2.6.27+ and glibc 2.9 and macOS 27.0;
11883+ * it needs runtime detection for the case of running on older kernels.
11884+ * Values: -1: unknown; 0: doesn't work; 1: works
11885+ */
1187211886 static int dup3_works = -1;
11873- #endif
11887+ (void) dup3_works; // unused on some platforms
1187411888
1187511889 /* dup2() can fail with EINTR if the target FD is already open, because it
1187611890 * then has to be closed. See os_close_impl() for why we don't handle EINTR
@@ -11910,17 +11924,23 @@ os_dup2_impl(PyObject *module, int fd, int fd2, int inheritable)
1191011924
1191111925#ifdef HAVE_DUP3
1191211926 if (!inheritable && dup3_works != 0) {
11913- Py_BEGIN_ALLOW_THREADS
11914- res = dup3(fd, fd2, O_CLOEXEC);
11915- Py_END_ALLOW_THREADS
11916- if (res < 0) {
11917- if (dup3_works == -1)
11918- dup3_works = (errno != ENOSYS);
11919- if (dup3_works) {
11920- posix_error();
11921- return -1;
11927+ if (HAVE_DUP3_RUNTIME) {
11928+ Py_BEGIN_ALLOW_THREADS
11929+ res = dup3(fd, fd2, O_CLOEXEC);
11930+ Py_END_ALLOW_THREADS
11931+ if (res < 0) {
11932+ if (dup3_works == -1) {
11933+ dup3_works = (errno != ENOSYS);
11934+ }
11935+ if (dup3_works) {
11936+ posix_error();
11937+ return -1;
11938+ }
1192211939 }
1192311940 }
11941+ else {
11942+ dup3_works = 0;
11943+ }
1192411944 }
1192511945
1192611946 if (inheritable || dup3_works == 0)
@@ -12761,7 +12781,14 @@ os_pipe_impl(PyObject *module)
1276112781 SECURITY_ATTRIBUTES attr;
1276212782 BOOL ok;
1276312783#else
12764- int res;
12784+ int res = -1;
12785+
12786+ /* pipe2() is available on some newer linux/glibc & macOS;
12787+ * it needs runtime detection for the case of running on older kernels.
12788+ * Values: -1: unknown; 0: doesn't work; 1: works
12789+ */
12790+ static int pipe2_works = -1;
12791+ (void) pipe2_works; // unused on some platforms
1276512792#endif
1276612793
1276712794#ifdef MS_WINDOWS
@@ -12787,11 +12814,27 @@ os_pipe_impl(PyObject *module)
1278712814#else
1278812815
1278912816#ifdef HAVE_PIPE2
12790- Py_BEGIN_ALLOW_THREADS
12791- res = pipe2(fds, O_CLOEXEC);
12792- Py_END_ALLOW_THREADS
12817+ if (pipe2_works != 0) {
12818+ if (HAVE_PIPE2_RUNTIME) {
12819+ Py_BEGIN_ALLOW_THREADS
12820+ res = pipe2(fds, O_CLOEXEC);
12821+ Py_END_ALLOW_THREADS
12822+ if (pipe2_works == -1) {
12823+ if (res != 0 && errno == ENOSYS) {
12824+ pipe2_works = 0;
12825+ }
12826+ else {
12827+ // pipe2 is present but this call failed
12828+ pipe2_works = 1;
12829+ }
12830+ }
12831+ }
12832+ else {
12833+ pipe2_works = 0;
12834+ }
12835+ }
1279312836
12794- if (res != 0 && errno == ENOSYS )
12837+ if (pipe2_works == 0 )
1279512838 {
1279612839#endif
1279712840 Py_BEGIN_ALLOW_THREADS
@@ -12814,8 +12857,9 @@ os_pipe_impl(PyObject *module)
1281412857 }
1281512858#endif
1281612859
12817- if (res != 0)
12860+ if (res != 0) {
1281812861 return PyErr_SetFromErrno(PyExc_OSError);
12862+ }
1281912863#endif /* !MS_WINDOWS */
1282012864 return Py_BuildValue("(ii)", fds[0], fds[1]);
1282112865}
@@ -12845,9 +12889,17 @@ os_pipe2_impl(PyObject *module, int flags)
1284512889 int fds[2];
1284612890 int res;
1284712891
12848- res = pipe2(fds, flags);
12849- if (res != 0)
12892+ if (HAVE_PIPE2_RUNTIME) {
12893+ res = pipe2(fds, flags);
12894+ }
12895+ else {
12896+ res = -1;
12897+ errno = ENOSYS;
12898+ }
12899+ if (res != 0) {
1285012900 return posix_error();
12901+ }
12902+
1285112903 return Py_BuildValue("(ii)", fds[0], fds[1]);
1285212904}
1285312905#endif /* HAVE_PIPE2 */
@@ -18844,6 +18896,22 @@ posixmodule_exec(PyObject *m)
1884418896 }
1884518897#endif
1884618898
18899+ #if HAVE_PIPE2
18900+ if (HAVE_PIPE2_RUNTIME) {
18901+ // Do nothing. (`__builtin_available` doesn't allow `!`; see
18902+ // "using negations" in a comment above.)
18903+ }
18904+ else {
18905+ PyObject* dct = PyModule_GetDict(m);
18906+ if (dct == NULL) {
18907+ return -1;
18908+ }
18909+ if (PyDict_PopString(dct, "pipe2", NULL) < 0) {
18910+ return -1;
18911+ }
18912+ }
18913+ #endif
18914+
1884718915 /* Initialize environ dictionary */
1884818916 if (PyModule_Add(m, "environ", convertenviron()) != 0) {
1884918917 return -1;
0 commit comments