Issue #23836: Add _Py_write_noraise() function

Helper to write() which retries write() if it is interrupted by a signal (fails
with EINTR).
This commit is contained in:
Victor Stinner 2015-04-01 18:34:45 +02:00
parent 81541f4480
commit 82c3e4599d
2 changed files with 88 additions and 48 deletions

View File

@ -84,6 +84,11 @@ PyAPI_FUNC(Py_ssize_t) _Py_write(
const void *buf,
size_t count);
PyAPI_FUNC(Py_ssize_t) _Py_write_noraise(
int fd,
const void *buf,
size_t count);
#ifdef HAVE_READLINK
PyAPI_FUNC(int) _Py_wreadlink(
const wchar_t *path,

View File

@ -1124,18 +1124,18 @@ _Py_fopen_obj(PyObject *path, const char *mode)
}
/* Read count bytes from fd into buf.
*
* On success, return the number of read bytes, it can be lower than count.
* If the current file offset is at or past the end of file, no bytes are read,
* and read() returns zero.
*
* On error, raise an exception, set errno and return -1.
*
* When interrupted by a signal (read() fails with EINTR), retry the syscall.
* If the Python signal handler raises an exception, the function returns -1
* (the syscall is not retried).
*
* The GIL must be held. */
On success, return the number of read bytes, it can be lower than count.
If the current file offset is at or past the end of file, no bytes are read,
and read() returns zero.
On error, raise an exception, set errno and return -1.
When interrupted by a signal (read() fails with EINTR), retry the syscall.
If the Python signal handler raises an exception, the function returns -1
(the syscall is not retried).
Release the GIL to call read(). The caller must hold the GIL. */
Py_ssize_t
_Py_read(int fd, void *buf, size_t count)
{
@ -1200,34 +1200,20 @@ _Py_read(int fd, void *buf, size_t count)
return n;
}
/* Write count bytes of buf into fd.
*
* -On success, return the number of written bytes, it can be lower than count
* including 0
* - On error, raise an exception, set errno and return -1.
*
* When interrupted by a signal (write() fails with EINTR), retry the syscall.
* If the Python signal handler raises an exception, the function returns -1
* (the syscall is not retried).
*
* The GIL must be held. */
Py_ssize_t
_Py_write(int fd, const void *buf, size_t count)
static Py_ssize_t
_Py_write_impl(int fd, const void *buf, size_t count, int gil_held)
{
Py_ssize_t n;
int err;
int async_err = 0;
/* _Py_write() must not be called with an exception set, otherwise the
* caller may think that write() was interrupted by a signal and the signal
* handler raised an exception. */
assert(!PyErr_Occurred());
if (!_PyVerify_fd(fd)) {
/* save/restore errno because PyErr_SetFromErrno() can modify it */
err = errno;
PyErr_SetFromErrno(PyExc_OSError);
errno = err;
if (gil_held) {
/* save/restore errno because PyErr_SetFromErrno() can modify it */
err = errno;
PyErr_SetFromErrno(PyExc_OSError);
errno = err;
}
return -1;
}
@ -1249,30 +1235,45 @@ _Py_write(int fd, const void *buf, size_t count)
}
#endif
do {
Py_BEGIN_ALLOW_THREADS
errno = 0;
if (gil_held) {
do {
Py_BEGIN_ALLOW_THREADS
errno = 0;
#ifdef MS_WINDOWS
n = write(fd, buf, (int)count);
n = write(fd, buf, (int)count);
#else
n = write(fd, buf, count);
n = write(fd, buf, count);
#endif
/* save/restore errno because PyErr_CheckSignals()
* and PyErr_SetFromErrno() can modify it */
err = errno;
Py_END_ALLOW_THREADS
} while (n < 0 && errno == EINTR &&
!(async_err = PyErr_CheckSignals()));
/* save/restore errno because PyErr_CheckSignals()
* and PyErr_SetFromErrno() can modify it */
err = errno;
Py_END_ALLOW_THREADS
} while (n < 0 && err == EINTR &&
!(async_err = PyErr_CheckSignals()));
}
else {
do {
errno = 0;
#ifdef MS_WINDOWS
n = write(fd, buf, (int)count);
#else
n = write(fd, buf, count);
#endif
err = errno;
} while (n < 0 && err == EINTR);
}
if (async_err) {
/* write() was interrupted by a signal (failed with EINTR)
* and the Python signal handler raised an exception */
and the Python signal handler raised an exception (if gil_held is
nonzero). */
errno = err;
assert(errno == EINTR && PyErr_Occurred());
assert(errno == EINTR && (!gil_held || PyErr_Occurred()));
return -1;
}
if (n < 0) {
PyErr_SetFromErrno(PyExc_OSError);
if (gil_held)
PyErr_SetFromErrno(PyExc_OSError);
errno = err;
return -1;
}
@ -1280,6 +1281,40 @@ _Py_write(int fd, const void *buf, size_t count)
return n;
}
/* Write count bytes of buf into fd.
On success, return the number of written bytes, it can be lower than count
including 0. On error, raise an exception, set errno and return -1.
When interrupted by a signal (write() fails with EINTR), retry the syscall.
If the Python signal handler raises an exception, the function returns -1
(the syscall is not retried).
Release the GIL to call write(). The caller must hold the GIL. */
Py_ssize_t
_Py_write(int fd, const void *buf, size_t count)
{
/* _Py_write() must not be called with an exception set, otherwise the
* caller may think that write() was interrupted by a signal and the signal
* handler raised an exception. */
assert(!PyErr_Occurred());
return _Py_write_impl(fd, buf, count, 1);
}
/* Write count bytes of buf into fd.
*
* On success, return the number of written bytes, it can be lower than count
* including 0. On error, set errno and return -1.
*
* When interrupted by a signal (write() fails with EINTR), retry the syscall
* without calling the Python signal handler. */
Py_ssize_t
_Py_write_noraise(int fd, const void *buf, size_t count)
{
return _Py_write_impl(fd, buf, count, 0);
}
#ifdef HAVE_READLINK
/* Read value of symbolic link. Encode the path to the locale encoding, decode