Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Take advantage of PyErr_CheckSignals now being callable without the GIL.
The source tree contains dozens of loops of this form:

    int res;
    do {
        Py_BEGIN_ALLOW_THREADS
        res = some_system_call(arguments...);
        Py_END_ALLOW_THREADS
    } while (res < 0 && errno == EINTR && !PyErr_CheckSignals());

Now that it is possible to call PyErr_CheckSignals without holding the
GIL, the locking operations can be moved out of the loop:

    Py_BEGIN_ALLOW_THREADS
    do {
        res = some_system_call(arguments...);
    } while (res < 0 && errno == EINTR && !PyErr_CheckSignals());
    Py_END_ALLOW_THREADS

This demonstrates the motivation for making it possible to call
PyErr_CheckSignals without holding the GIL.  It shouldn’t make any
measurable difference performance-wise for _these_ loops, which almost
never actually cycle; but for loops that do cycle many times it’s very
much desirable to not take and release the GIL every time through.

In some cases I also moved uses of _Py_(BEGIN|END)_SUPPRESS_IPH, which
is often paired with Py_(BEGIN|END)_ALLOW_THREADS, to keep the pairing
intact.  It was already considered safe to call PyErr_CheckSignals
from both inside and outside an IPH suppression region.

More could be done in this vein: I didn’t change any loops where the
inside of the loop was more complicated than a single system call,
_except_ that I did refactor py_getentropy and py_getrandom (in
bootstrap_hash.c) to make it possible to move the unlock and lock
outside the loop, demonstrating a more complicated case.
  • Loading branch information
zackw committed May 5, 2025
commit 3e25a9350a6b32e297e2785e8fb15ca522df8c0d
4 changes: 2 additions & 2 deletions Modules/_io/fileio.c
Original file line number Diff line number Diff line change
Expand Up @@ -403,16 +403,16 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode,

errno = 0;
if (opener == Py_None) {
Py_BEGIN_ALLOW_THREADS
do {
Py_BEGIN_ALLOW_THREADS
#ifdef MS_WINDOWS
self->fd = _wopen(widename, flags, 0666);
#else
self->fd = open(name, flags, 0666);
#endif
Py_END_ALLOW_THREADS
} while (self->fd < 0 && errno == EINTR &&
!(async_err = PyErr_CheckSignals()));
Py_END_ALLOW_THREADS

if (async_err)
goto error;
Expand Down
2 changes: 0 additions & 2 deletions Modules/_io/winconsoleio.c
Original file line number Diff line number Diff line change
Expand Up @@ -647,9 +647,7 @@ read_console_w(HANDLE handle, DWORD maxlen, DWORD *readlen) {
if (WaitForSingleObjectEx(hInterruptEvent, 100, FALSE)
== WAIT_OBJECT_0) {
ResetEvent(hInterruptEvent);
Py_BLOCK_THREADS
sig = PyErr_CheckSignals();
Py_UNBLOCK_THREADS
if (sig < 0)
break;
}
Expand Down
8 changes: 4 additions & 4 deletions Modules/_multiprocessing/posixshmem.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ _posixshmem_shm_open_impl(PyObject *module, PyObject *path, int flags,
PyErr_SetString(PyExc_ValueError, "embedded null character");
return -1;
}
Py_BEGIN_ALLOW_THREADS
do {
Py_BEGIN_ALLOW_THREADS
fd = shm_open(name, flags, mode);
Py_END_ALLOW_THREADS
} while (fd < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
Py_END_ALLOW_THREADS

if (fd < 0) {
if (!async_err)
Expand Down Expand Up @@ -102,11 +102,11 @@ _posixshmem_shm_unlink_impl(PyObject *module, PyObject *path)
PyErr_SetString(PyExc_ValueError, "embedded null character");
return NULL;
}
Py_BEGIN_ALLOW_THREADS
do {
Py_BEGIN_ALLOW_THREADS
rv = shm_unlink(name);
Py_END_ALLOW_THREADS
} while (rv < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
Py_END_ALLOW_THREADS

if (rv < 0) {
if (!async_err)
Expand Down
4 changes: 2 additions & 2 deletions Modules/_multiprocessing/semaphore.c
Original file line number Diff line number Diff line change
Expand Up @@ -356,19 +356,19 @@ _multiprocessing_SemLock_acquire_impl(SemLockObject *self, int blocking,

if (res < 0 && errno == EAGAIN && blocking) {
/* Couldn't acquire immediately, need to block */
Py_BEGIN_ALLOW_THREADS
do {
Py_BEGIN_ALLOW_THREADS
if (!use_deadline) {
res = sem_wait(self->handle);
}
else {
res = sem_timedwait(self->handle, &deadline);
}
Py_END_ALLOW_THREADS
err = errno;
if (res == MP_EXCEPTION_HAS_BEEN_SET)
break;
} while (res < 0 && errno == EINTR && !PyErr_CheckSignals());
Py_END_ALLOW_THREADS
}

if (res < 0) {
Expand Down
32 changes: 16 additions & 16 deletions Modules/fcntlmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg)
}
}

Py_BEGIN_ALLOW_THREADS
do {
Py_BEGIN_ALLOW_THREADS
ret = fcntl(fd, code, (int)int_arg);
Py_END_ALLOW_THREADS
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
Py_END_ALLOW_THREADS
if (ret < 0) {
return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL;
}
Expand All @@ -103,11 +103,11 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg)
memcpy(buf + len, guard, GUARDSZ);
PyBuffer_Release(&view);

Py_BEGIN_ALLOW_THREADS
do {
Py_BEGIN_ALLOW_THREADS
ret = fcntl(fd, code, buf);
Py_END_ALLOW_THREADS
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
Py_END_ALLOW_THREADS
if (ret < 0) {
return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL;
}
Expand Down Expand Up @@ -195,11 +195,11 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned long code, PyObject *arg,
}
}

Py_BEGIN_ALLOW_THREADS
do {
Py_BEGIN_ALLOW_THREADS
ret = ioctl(fd, code, int_arg);
Py_END_ALLOW_THREADS
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
Py_END_ALLOW_THREADS
if (ret < 0) {
return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL;
}
Expand All @@ -219,11 +219,11 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned long code, PyObject *arg,
memcpy(buf + len, guard, GUARDSZ);
ptr = buf;
}
Py_BEGIN_ALLOW_THREADS
do {
Py_BEGIN_ALLOW_THREADS
ret = ioctl(fd, code, ptr);
Py_END_ALLOW_THREADS
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
Py_END_ALLOW_THREADS
if (ret < 0) {
if (!async_err) {
PyErr_SetFromErrno(PyExc_OSError);
Expand Down Expand Up @@ -261,11 +261,11 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned long code, PyObject *arg,
memcpy(buf + len, guard, GUARDSZ);
PyBuffer_Release(&view);

Py_BEGIN_ALLOW_THREADS
do {
Py_BEGIN_ALLOW_THREADS
ret = ioctl(fd, code, buf);
Py_END_ALLOW_THREADS
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
Py_END_ALLOW_THREADS
if (ret < 0) {
return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL;
}
Expand Down Expand Up @@ -308,11 +308,11 @@ fcntl_flock_impl(PyObject *module, int fd, int code)
}

#ifdef HAVE_FLOCK
Py_BEGIN_ALLOW_THREADS
do {
Py_BEGIN_ALLOW_THREADS
ret = flock(fd, code);
Py_END_ALLOW_THREADS
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
Py_END_ALLOW_THREADS
#else

#ifndef LOCK_SH
Expand All @@ -335,11 +335,11 @@ fcntl_flock_impl(PyObject *module, int fd, int code)
return NULL;
}
l.l_whence = l.l_start = l.l_len = 0;
Py_BEGIN_ALLOW_THREADS
do {
Py_BEGIN_ALLOW_THREADS
ret = fcntl(fd, (code & LOCK_NB) ? F_SETLK : F_SETLKW, &l);
Py_END_ALLOW_THREADS
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
Py_END_ALLOW_THREADS
}
#endif /* HAVE_FLOCK */
if (ret < 0) {
Expand Down Expand Up @@ -439,11 +439,11 @@ fcntl_lockf_impl(PyObject *module, int fd, int code, PyObject *lenobj,
return NULL;
}
l.l_whence = whence;
Py_BEGIN_ALLOW_THREADS
do {
Py_BEGIN_ALLOW_THREADS
ret = fcntl(fd, (code & LOCK_NB) ? F_SETLK : F_SETLKW, &l);
Py_END_ALLOW_THREADS
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
Py_END_ALLOW_THREADS
}
if (ret < 0) {
return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL;
Expand Down
Loading