Skip to content

Commit 82c3e45

Browse files
committed
Issue #23836: Add _Py_write_noraise() function
Helper to write() which retries write() if it is interrupted by a signal (fails with EINTR).
1 parent 81541f4 commit 82c3e45

File tree

2 files changed

+88
-48
lines changed

2 files changed

+88
-48
lines changed

Diff for: Include/fileutils.h

+5
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ PyAPI_FUNC(Py_ssize_t) _Py_write(
8484
const void *buf,
8585
size_t count);
8686

87+
PyAPI_FUNC(Py_ssize_t) _Py_write_noraise(
88+
int fd,
89+
const void *buf,
90+
size_t count);
91+
8792
#ifdef HAVE_READLINK
8893
PyAPI_FUNC(int) _Py_wreadlink(
8994
const wchar_t *path,

Diff for: Python/fileutils.c

+83-48
Original file line numberDiff line numberDiff line change
@@ -1124,18 +1124,18 @@ _Py_fopen_obj(PyObject *path, const char *mode)
11241124
}
11251125

11261126
/* Read count bytes from fd into buf.
1127-
*
1128-
* On success, return the number of read bytes, it can be lower than count.
1129-
* If the current file offset is at or past the end of file, no bytes are read,
1130-
* and read() returns zero.
1131-
*
1132-
* On error, raise an exception, set errno and return -1.
1133-
*
1134-
* When interrupted by a signal (read() fails with EINTR), retry the syscall.
1135-
* If the Python signal handler raises an exception, the function returns -1
1136-
* (the syscall is not retried).
1137-
*
1138-
* The GIL must be held. */
1127+
1128+
On success, return the number of read bytes, it can be lower than count.
1129+
If the current file offset is at or past the end of file, no bytes are read,
1130+
and read() returns zero.
1131+
1132+
On error, raise an exception, set errno and return -1.
1133+
1134+
When interrupted by a signal (read() fails with EINTR), retry the syscall.
1135+
If the Python signal handler raises an exception, the function returns -1
1136+
(the syscall is not retried).
1137+
1138+
Release the GIL to call read(). The caller must hold the GIL. */
11391139
Py_ssize_t
11401140
_Py_read(int fd, void *buf, size_t count)
11411141
{
@@ -1200,34 +1200,20 @@ _Py_read(int fd, void *buf, size_t count)
12001200
return n;
12011201
}
12021202

1203-
/* Write count bytes of buf into fd.
1204-
*
1205-
* -On success, return the number of written bytes, it can be lower than count
1206-
* including 0
1207-
* - On error, raise an exception, set errno and return -1.
1208-
*
1209-
* When interrupted by a signal (write() fails with EINTR), retry the syscall.
1210-
* If the Python signal handler raises an exception, the function returns -1
1211-
* (the syscall is not retried).
1212-
*
1213-
* The GIL must be held. */
1214-
Py_ssize_t
1215-
_Py_write(int fd, const void *buf, size_t count)
1203+
static Py_ssize_t
1204+
_Py_write_impl(int fd, const void *buf, size_t count, int gil_held)
12161205
{
12171206
Py_ssize_t n;
12181207
int err;
12191208
int async_err = 0;
12201209

1221-
/* _Py_write() must not be called with an exception set, otherwise the
1222-
* caller may think that write() was interrupted by a signal and the signal
1223-
* handler raised an exception. */
1224-
assert(!PyErr_Occurred());
1225-
12261210
if (!_PyVerify_fd(fd)) {
1227-
/* save/restore errno because PyErr_SetFromErrno() can modify it */
1228-
err = errno;
1229-
PyErr_SetFromErrno(PyExc_OSError);
1230-
errno = err;
1211+
if (gil_held) {
1212+
/* save/restore errno because PyErr_SetFromErrno() can modify it */
1213+
err = errno;
1214+
PyErr_SetFromErrno(PyExc_OSError);
1215+
errno = err;
1216+
}
12311217
return -1;
12321218
}
12331219

@@ -1249,37 +1235,86 @@ _Py_write(int fd, const void *buf, size_t count)
12491235
}
12501236
#endif
12511237

1252-
do {
1253-
Py_BEGIN_ALLOW_THREADS
1254-
errno = 0;
1238+
if (gil_held) {
1239+
do {
1240+
Py_BEGIN_ALLOW_THREADS
1241+
errno = 0;
12551242
#ifdef MS_WINDOWS
1256-
n = write(fd, buf, (int)count);
1243+
n = write(fd, buf, (int)count);
12571244
#else
1258-
n = write(fd, buf, count);
1245+
n = write(fd, buf, count);
12591246
#endif
1260-
/* save/restore errno because PyErr_CheckSignals()
1261-
* and PyErr_SetFromErrno() can modify it */
1262-
err = errno;
1263-
Py_END_ALLOW_THREADS
1264-
} while (n < 0 && errno == EINTR &&
1265-
!(async_err = PyErr_CheckSignals()));
1247+
/* save/restore errno because PyErr_CheckSignals()
1248+
* and PyErr_SetFromErrno() can modify it */
1249+
err = errno;
1250+
Py_END_ALLOW_THREADS
1251+
} while (n < 0 && err == EINTR &&
1252+
!(async_err = PyErr_CheckSignals()));
1253+
}
1254+
else {
1255+
do {
1256+
errno = 0;
1257+
#ifdef MS_WINDOWS
1258+
n = write(fd, buf, (int)count);
1259+
#else
1260+
n = write(fd, buf, count);
1261+
#endif
1262+
err = errno;
1263+
} while (n < 0 && err == EINTR);
1264+
}
12661265

12671266
if (async_err) {
12681267
/* write() was interrupted by a signal (failed with EINTR)
1269-
* and the Python signal handler raised an exception */
1268+
and the Python signal handler raised an exception (if gil_held is
1269+
nonzero). */
12701270
errno = err;
1271-
assert(errno == EINTR && PyErr_Occurred());
1271+
assert(errno == EINTR && (!gil_held || PyErr_Occurred()));
12721272
return -1;
12731273
}
12741274
if (n < 0) {
1275-
PyErr_SetFromErrno(PyExc_OSError);
1275+
if (gil_held)
1276+
PyErr_SetFromErrno(PyExc_OSError);
12761277
errno = err;
12771278
return -1;
12781279
}
12791280

12801281
return n;
12811282
}
12821283

1284+
/* Write count bytes of buf into fd.
1285+
1286+
On success, return the number of written bytes, it can be lower than count
1287+
including 0. On error, raise an exception, set errno and return -1.
1288+
1289+
When interrupted by a signal (write() fails with EINTR), retry the syscall.
1290+
If the Python signal handler raises an exception, the function returns -1
1291+
(the syscall is not retried).
1292+
1293+
Release the GIL to call write(). The caller must hold the GIL. */
1294+
Py_ssize_t
1295+
_Py_write(int fd, const void *buf, size_t count)
1296+
{
1297+
/* _Py_write() must not be called with an exception set, otherwise the
1298+
* caller may think that write() was interrupted by a signal and the signal
1299+
* handler raised an exception. */
1300+
assert(!PyErr_Occurred());
1301+
1302+
return _Py_write_impl(fd, buf, count, 1);
1303+
}
1304+
1305+
/* Write count bytes of buf into fd.
1306+
*
1307+
* On success, return the number of written bytes, it can be lower than count
1308+
* including 0. On error, set errno and return -1.
1309+
*
1310+
* When interrupted by a signal (write() fails with EINTR), retry the syscall
1311+
* without calling the Python signal handler. */
1312+
Py_ssize_t
1313+
_Py_write_noraise(int fd, const void *buf, size_t count)
1314+
{
1315+
return _Py_write_impl(fd, buf, count, 0);
1316+
}
1317+
12831318
#ifdef HAVE_READLINK
12841319

12851320
/* Read value of symbolic link. Encode the path to the locale encoding, decode

0 commit comments

Comments
 (0)