Skip to content

Commit 9b5d911

Browse files
committed
bpo-32186: Release the GIL during fstat and lseek calls
In fileio, there were 3 fstat() calls and one lseek() call that did not release the GIL during the call. This can cause all threads to hang for unlimited time when using io.FileIO with inaccessible NFS server.
1 parent 56e444f commit 9b5d911

File tree

1 file changed

+44
-11
lines changed

1 file changed

+44
-11
lines changed

Modules/_io/fileio.c

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,15 @@ dircheck(fileio* self, PyObject *nameobj)
146146
{
147147
#if defined(HAVE_FSTAT) && defined(S_IFDIR) && defined(EISDIR)
148148
struct stat buf;
149+
int res;
149150
if (self->fd < 0)
150151
return 0;
151-
if (fstat(self->fd, &buf) == 0 && S_ISDIR(buf.st_mode)) {
152+
153+
Py_BEGIN_ALLOW_THREADS
154+
res = fstat(self->fd, &buf);
155+
Py_END_ALLOW_THREADS
156+
157+
if (res == 0 && S_ISDIR(buf.st_mode)) {
152158
errno = EISDIR;
153159
PyErr_SetFromErrnoWithFilenameObject(PyExc_IOError, nameobj);
154160
return -1;
@@ -162,17 +168,34 @@ check_fd(int fd)
162168
{
163169
#if defined(HAVE_FSTAT)
164170
struct stat buf;
165-
if (!_PyVerify_fd(fd) || (fstat(fd, &buf) < 0 && errno == EBADF)) {
166-
PyObject *exc;
167-
char *msg = strerror(EBADF);
168-
exc = PyObject_CallFunction(PyExc_OSError, "(is)",
169-
EBADF, msg);
170-
PyErr_SetObject(PyExc_OSError, exc);
171-
Py_XDECREF(exc);
172-
return -1;
171+
int res;
172+
PyObject *exc;
173+
char *msg;
174+
175+
if (!_PyVerify_fd(fd)) {
176+
goto badfd;
173177
}
174-
#endif
178+
179+
Py_BEGIN_ALLOW_THREADS
180+
res = fstat(fd, &buf);
181+
Py_END_ALLOW_THREADS
182+
183+
if (res < 0 && errno == EBADF) {
184+
goto badfd;
185+
}
186+
175187
return 0;
188+
189+
badfd:
190+
msg = strerror(EBADF);
191+
exc = PyObject_CallFunction(PyExc_OSError, "(is)",
192+
EBADF, msg);
193+
PyErr_SetObject(PyExc_OSError, exc);
194+
Py_XDECREF(exc);
195+
return -1;
196+
#else
197+
return 0;
198+
#endif
176199
}
177200

178201

@@ -519,9 +542,19 @@ new_buffersize(fileio *self, size_t currentsize)
519542
#ifdef HAVE_FSTAT
520543
off_t pos, end;
521544
struct stat st;
522-
if (fstat(self->fd, &st) == 0) {
545+
int res;
546+
547+
Py_BEGIN_ALLOW_THREADS
548+
res = fstat(self->fd, &st);
549+
Py_END_ALLOW_THREADS
550+
551+
if (res == 0) {
523552
end = st.st_size;
553+
554+
Py_BEGIN_ALLOW_THREADS
524555
pos = lseek(self->fd, 0L, SEEK_CUR);
556+
Py_END_ALLOW_THREADS
557+
525558
/* Files claiming a size smaller than SMALLCHUNK may
526559
actually be streaming pseudo-files. In this case, we
527560
apply the more aggressive algorithm below.

0 commit comments

Comments
 (0)