Skip to content

Commit c230fde

Browse files
authored
bpo-40422: create a common _Py_closerange API (GH-19754)
Such an API can be used both for os.closerange and subprocess. For the latter, this yields potential improvement for platforms that have fdwalk but wouldn't have used it there. This will prove even more beneficial later for platforms that have close_range(2), as the new API will prefer that over all else if it's available. The new API is structured to look more like close_range(2), closing from [start, end] rather than the [low, high) of os.closerange(). Automerge-Triggered-By: @gpshead
1 parent d5752aa commit c230fde

File tree

4 files changed

+51
-37
lines changed

4 files changed

+51
-37
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add `_Py_closerange` function to provide performant closing of a range of file descriptors.

Modules/_posixsubprocess.c

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -250,29 +250,18 @@ _close_fds_by_brute_force(long start_fd, PyObject *py_fds_to_keep)
250250
long end_fd = safe_get_max_fd();
251251
Py_ssize_t num_fds_to_keep = PyTuple_GET_SIZE(py_fds_to_keep);
252252
Py_ssize_t keep_seq_idx;
253-
int fd_num;
254253
/* As py_fds_to_keep is sorted we can loop through the list closing
255254
* fds in between any in the keep list falling within our range. */
256255
for (keep_seq_idx = 0; keep_seq_idx < num_fds_to_keep; ++keep_seq_idx) {
257256
PyObject* py_keep_fd = PyTuple_GET_ITEM(py_fds_to_keep, keep_seq_idx);
258257
int keep_fd = PyLong_AsLong(py_keep_fd);
259258
if (keep_fd < start_fd)
260259
continue;
261-
for (fd_num = start_fd; fd_num < keep_fd; ++fd_num) {
262-
close(fd_num);
263-
}
260+
_Py_closerange(start_fd, keep_fd - 1);
264261
start_fd = keep_fd + 1;
265262
}
266263
if (start_fd <= end_fd) {
267-
#if defined(__FreeBSD__)
268-
/* Any errors encountered while closing file descriptors are ignored */
269-
closefrom(start_fd);
270-
#else
271-
for (fd_num = start_fd; fd_num < end_fd; ++fd_num) {
272-
/* Ignore errors */
273-
(void)close(fd_num);
274-
}
275-
#endif
264+
_Py_closerange(start_fd, end_fd);
276265
}
277266
}
278267

Modules/posixmodule.c

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8740,8 +8740,23 @@ os_close_impl(PyObject *module, int fd)
87408740
Py_RETURN_NONE;
87418741
}
87428742

8743+
/* Our selection logic for which function to use is as follows:
8744+
* 1. If closefrom(2) is available, we'll attempt to use that next if we're
8745+
* closing up to sysconf(_SC_OPEN_MAX).
8746+
* 1a. Fallback to fdwalk(3) if we're not closing up to sysconf(_SC_OPEN_MAX),
8747+
* as that will be more performant if the range happens to have any chunk of
8748+
* non-opened fd in the middle.
8749+
* 1b. If fdwalk(3) isn't available, just do a plain close(2) loop.
8750+
*/
8751+
#ifdef __FreeBSD__
8752+
#define USE_CLOSEFROM
8753+
#endif /* __FreeBSD__ */
87438754

87448755
#ifdef HAVE_FDWALK
8756+
#define USE_FDWALK
8757+
#endif /* HAVE_FDWALK */
8758+
8759+
#ifdef USE_FDWALK
87458760
static int
87468761
_fdwalk_close_func(void *lohi, int fd)
87478762
{
@@ -8757,7 +8772,36 @@ _fdwalk_close_func(void *lohi, int fd)
87578772
}
87588773
return 0;
87598774
}
8760-
#endif /* HAVE_FDWALK */
8775+
#endif /* USE_FDWALK */
8776+
8777+
/* Closes all file descriptors in [first, last], ignoring errors. */
8778+
void
8779+
_Py_closerange(int first, int last)
8780+
{
8781+
first = Py_MAX(first, 0);
8782+
#ifdef USE_CLOSEFROM
8783+
if (last >= sysconf(_SC_OPEN_MAX)) {
8784+
/* Any errors encountered while closing file descriptors are ignored */
8785+
closefrom(first);
8786+
}
8787+
else
8788+
#endif /* USE_CLOSEFROM */
8789+
#ifdef USE_FDWALK
8790+
{
8791+
int lohi[2];
8792+
lohi[0] = first;
8793+
lohi[1] = last + 1;
8794+
fdwalk(_fdwalk_close_func, lohi);
8795+
}
8796+
#else
8797+
{
8798+
for (int i = first; i <= last; i++) {
8799+
/* Ignore errors */
8800+
(void)close(i);
8801+
}
8802+
}
8803+
#endif /* USE_FDWALK */
8804+
}
87618805

87628806
/*[clinic input]
87638807
os.closerange
@@ -8773,31 +8817,9 @@ static PyObject *
87738817
os_closerange_impl(PyObject *module, int fd_low, int fd_high)
87748818
/*[clinic end generated code: output=0ce5c20fcda681c2 input=5855a3d053ebd4ec]*/
87758819
{
8776-
#ifdef HAVE_FDWALK
8777-
int lohi[2];
8778-
#endif
87798820
Py_BEGIN_ALLOW_THREADS
87808821
_Py_BEGIN_SUPPRESS_IPH
8781-
#ifdef HAVE_FDWALK
8782-
lohi[0] = Py_MAX(fd_low, 0);
8783-
lohi[1] = fd_high;
8784-
fdwalk(_fdwalk_close_func, lohi);
8785-
#else
8786-
fd_low = Py_MAX(fd_low, 0);
8787-
#ifdef __FreeBSD__
8788-
if (fd_high >= sysconf(_SC_OPEN_MAX)) {
8789-
/* Any errors encountered while closing file descriptors are ignored */
8790-
closefrom(fd_low);
8791-
}
8792-
else
8793-
#endif
8794-
{
8795-
for (int i = fd_low; i < fd_high; i++) {
8796-
/* Ignore errors */
8797-
(void)close(i);
8798-
}
8799-
}
8800-
#endif
8822+
_Py_closerange(fd_low, fd_high - 1);
88018823
_Py_END_SUPPRESS_IPH
88028824
Py_END_ALLOW_THREADS
88038825
Py_RETURN_NONE;

Modules/posixmodule.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ PyAPI_FUNC(int) _Py_Sigset_Converter(PyObject *, void *);
2828
#endif /* HAVE_SIGSET_T */
2929
#endif /* Py_LIMITED_API */
3030

31+
PyAPI_FUNC(void) _Py_closerange(int first, int last);
32+
3133
#ifdef __cplusplus
3234
}
3335
#endif

0 commit comments

Comments
 (0)