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