Thanks to visit codestin.com
Credit goes to github.com

Skip to content

gh-101881: os: support blocking functions on Windows #101882

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions Doc/library/os.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1091,13 +1091,17 @@ as internal buffering of data.

See also :func:`set_blocking` and :meth:`socket.socket.setblocking`.

.. availability:: Unix.
.. availability:: Unix, Windows.

The function is limited on Emscripten and WASI, see
:ref:`wasm-availability` for more information.

On Windows, this function is limited to pipes.

.. versionadded:: 3.5

.. versionchanged:: 3.12
Added support for pipes on Windows.

.. function:: isatty(fd, /)

Expand Down Expand Up @@ -1565,13 +1569,17 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo

See also :func:`get_blocking` and :meth:`socket.socket.setblocking`.

.. availability:: Unix.
.. availability:: Unix, Windows.

The function is limited on Emscripten and WASI, see
:ref:`wasm-availability` for more information.

On Windows, this function is limited to pipes.

.. versionadded:: 3.5

.. versionchanged:: 3.12
Added support for pipes on Windows.

.. data:: SF_NODISKIO
SF_MNOWAIT
Expand Down
4 changes: 2 additions & 2 deletions Include/internal/pycore_fileutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,11 @@ PyAPI_FUNC(int) _Py_set_inheritable_async_safe(int fd, int inheritable,

PyAPI_FUNC(int) _Py_dup(int fd);

#ifndef MS_WINDOWS
PyAPI_FUNC(int) _Py_get_blocking(int fd);

PyAPI_FUNC(int) _Py_set_blocking(int fd, int blocking);
#else /* MS_WINDOWS */

#ifdef MS_WINDOWS
PyAPI_FUNC(void*) _Py_get_osfhandle_noraise(int fd);

PyAPI_FUNC(void*) _Py_get_osfhandle(int fd);
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -4099,6 +4099,7 @@ def test_path_t_converter_and_custom_class(self):
@unittest.skipUnless(hasattr(os, 'get_blocking'),
'needs os.get_blocking() and os.set_blocking()')
@unittest.skipIf(support.is_emscripten, "Cannot unset blocking flag")
@unittest.skipIf(sys.platform == 'win32', 'Windows only supports blocking on pipes')
class BlockingTests(unittest.TestCase):
def test_blocking(self):
fd = os.open(__file__, os.O_RDONLY)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for the os.get_blocking() and os.set_blocking() functions on Windows.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Handle read and write operations on non-blocking pipes properly on Windows.
18 changes: 1 addition & 17 deletions Modules/clinic/posixmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -13930,7 +13930,6 @@ os_set_handle_inheritable_impl(PyObject *module, intptr_t handle,
}
#endif /* MS_WINDOWS */

#ifndef MS_WINDOWS
/*[clinic input]
os.get_blocking -> bool
fd: int
Expand Down Expand Up @@ -13978,7 +13977,6 @@ os_set_blocking_impl(PyObject *module, int fd, int blocking)
return NULL;
Py_RETURN_NONE;
}
#endif /* !MS_WINDOWS */


/*[clinic input]
Expand Down
93 changes: 91 additions & 2 deletions Python/fileutils.c
Original file line number Diff line number Diff line change
Expand Up @@ -1750,7 +1750,15 @@ _Py_read(int fd, void *buf, size_t count)
Py_BEGIN_ALLOW_THREADS
errno = 0;
#ifdef MS_WINDOWS
_doserrno = 0;
n = read(fd, buf, (int)count);
// read() on a non-blocking empty pipe fails with EINVAL, which is
// mapped from the Windows error code ERROR_NO_DATA.
if (n < 0 && errno == EINVAL) {
if (_doserrno == ERROR_NO_DATA) {
errno = EAGAIN;
}
}
#else
n = read(fd, buf, count);
#endif
Expand Down Expand Up @@ -1804,6 +1812,7 @@ _Py_write_impl(int fd, const void *buf, size_t count, int gil_held)
}
}
}

#endif
if (count > _PY_WRITE_MAX) {
count = _PY_WRITE_MAX;
Expand All @@ -1814,7 +1823,18 @@ _Py_write_impl(int fd, const void *buf, size_t count, int gil_held)
Py_BEGIN_ALLOW_THREADS
errno = 0;
#ifdef MS_WINDOWS
n = write(fd, buf, (int)count);
// write() on a non-blocking pipe fails with ENOSPC on Windows if
// the pipe lacks available space for the entire buffer.
int c = (int)count;
do {
_doserrno = 0;
n = write(fd, buf, c);
if (n >= 0 || errno != ENOSPC || _doserrno != 0) {
break;
}
errno = EAGAIN;
c /= 2;
} while (c > 0);
#else
n = write(fd, buf, count);
#endif
Expand All @@ -1829,7 +1849,18 @@ _Py_write_impl(int fd, const void *buf, size_t count, int gil_held)
do {
errno = 0;
#ifdef MS_WINDOWS
n = write(fd, buf, (int)count);
// write() on a non-blocking pipe fails with ENOSPC on Windows if
// the pipe lacks available space for the entire buffer.
int c = (int)count;
do {
_doserrno = 0;
n = write(fd, buf, c);
if (n >= 0 || errno != ENOSPC || _doserrno != 0) {
break;
}
errno = EAGAIN;
c /= 2;
} while (c > 0);
#else
n = write(fd, buf, count);
#endif
Expand Down Expand Up @@ -2450,6 +2481,64 @@ _Py_set_blocking(int fd, int blocking)
return -1;
}
#else /* MS_WINDOWS */
int
_Py_get_blocking(int fd)
{
HANDLE handle;
DWORD mode;
BOOL success;

handle = _Py_get_osfhandle(fd);
if (handle == INVALID_HANDLE_VALUE) {
return -1;
}

Py_BEGIN_ALLOW_THREADS
success = GetNamedPipeHandleStateW(handle, &mode,
NULL, NULL, NULL, NULL, 0);
Py_END_ALLOW_THREADS

if (!success) {
PyErr_SetFromWindowsErr(0);
return -1;
}

return !(mode & PIPE_NOWAIT);
}

int
_Py_set_blocking(int fd, int blocking)
{
HANDLE handle;
DWORD mode;
BOOL success;

handle = _Py_get_osfhandle(fd);
if (handle == INVALID_HANDLE_VALUE) {
return -1;
}

Py_BEGIN_ALLOW_THREADS
success = GetNamedPipeHandleStateW(handle, &mode,
NULL, NULL, NULL, NULL, 0);
if (success) {
if (blocking) {
mode &= ~PIPE_NOWAIT;
}
else {
mode |= PIPE_NOWAIT;
}
success = SetNamedPipeHandleState(handle, &mode, NULL, NULL);
}
Py_END_ALLOW_THREADS

if (!success) {
PyErr_SetFromWindowsErr(0);
return -1;
}
return 0;
}

void*
_Py_get_osfhandle_noraise(int fd)
{
Expand Down