diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2015-04-01 16:34:45 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2015-04-01 16:34:45 (GMT) |
commit | 82c3e4599d067f7dc72cea1530d3a26ba1682028 (patch) | |
tree | fe7baed4b7cc0b58372f77c7bda499ef653ea0de | |
parent | 81541f448066142c04fae736c3dda23b184c32be (diff) | |
download | cpython-82c3e4599d067f7dc72cea1530d3a26ba1682028.zip cpython-82c3e4599d067f7dc72cea1530d3a26ba1682028.tar.gz cpython-82c3e4599d067f7dc72cea1530d3a26ba1682028.tar.bz2 |
Issue #23836: Add _Py_write_noraise() function
Helper to write() which retries write() if it is interrupted by a signal (fails
with EINTR).
-rw-r--r-- | Include/fileutils.h | 5 | ||||
-rw-r--r-- | Python/fileutils.c | 131 |
2 files changed, 88 insertions, 48 deletions
diff --git a/Include/fileutils.h b/Include/fileutils.h index 4fd3172..2430c26 100644 --- a/Include/fileutils.h +++ b/Include/fileutils.h @@ -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, diff --git a/Python/fileutils.c b/Python/fileutils.c index daaad2a..e4c5241 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -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 |