From e1981155063c82a45a209422ef8b1a2bbcff98bc Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Wed, 23 Sep 2026 09:07:15 +0000 Subject: [PATCH 1/3] gh-89977: Keep the GIL for short socket and epoll control calls --- Lib/test/test_epoll.py | 10 ++++++++++ Lib/test/test_socket.py | 12 ++++++++++++ .../2026-09-23-09-21-25.gh-issue-89977.fmia18.rst | 3 +++ Modules/selectmodule.c | 3 +-- Modules/socketmodule.c | 9 --------- 5 files changed, 26 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-09-23-09-21-25.gh-issue-89977.fmia18.rst diff --git a/Lib/test/test_epoll.py b/Lib/test/test_epoll.py index 51405ca7c9e57c3..97295fc98565eb5 100644 --- a/Lib/test/test_epoll.py +++ b/Lib/test/test_epoll.py @@ -238,6 +238,16 @@ def test_unregister_closed(self): ep.unregister(fd) self.assertEqual(cm.exception.errno, errno.EBADF) + def test_unregister_twice(self): + with select.epoll() as ep: + ep.register(self.serverSocket) + ep.unregister(self.serverSocket) + with self.assertRaises(OSError) as cm: + ep.unregister(self.serverSocket) + self.assertEqual(cm.exception.errno, errno.ENOENT) + ep.register(self.serverSocket) + ep.unregister(self.serverSocket) + def test_close(self): open_file = open(__file__, "rb") self.addCleanup(open_file.close) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index e4b3d848923f8c9..c1434b4ac1f8b45 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -945,6 +945,18 @@ def requireSocket(*args): class GeneralModuleTests(unittest.TestCase): + def test_socket_name_errors(self): + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.bind((socket_helper.HOSTv4, 0)) + self.assertGreater(sock.getsockname()[1], 0) + with self.assertRaises(OSError): + sock.getpeername() + + with self.assertRaises(OSError): + sock.getsockname() + with self.assertRaises(OSError): + sock.getpeername() + @unittest.skipUnless(_socket is not None, 'need _socket module') def test_socket_type(self): self.assertTrue(gc.is_tracked(_socket.socket)) diff --git a/Misc/NEWS.d/next/Library/2026-09-23-09-21-25.gh-issue-89977.fmia18.rst b/Misc/NEWS.d/next/Library/2026-09-23-09-21-25.gh-issue-89977.fmia18.rst new file mode 100644 index 000000000000000..68704b779a4e10f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-09-23-09-21-25.gh-issue-89977.fmia18.rst @@ -0,0 +1,3 @@ +Avoid releasing the GIL for :meth:`socket.socket.setblocking`, +:meth:`socket.socket.getsockname`, :meth:`socket.socket.getpeername`, +:func:`socket.gethostname`, and :meth:`select.epoll.unregister`. diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index b9fb7762e3dacdf..27de6f04d5d79f7 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -1521,9 +1521,8 @@ pyepoll_internal_ctl(int epfd, int op, int fd, unsigned int events) /* In kernel versions before 2.6.9, the EPOLL_CTL_DEL * operation required a non-NULL pointer in event, even * though this argument is ignored. */ - Py_BEGIN_ALLOW_THREADS + /* Removing a watch is normally quick, so keep the GIL. */ result = epoll_ctl(epfd, op, fd, &ev); - Py_END_ALLOW_THREADS break; default: result = -1; diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 61505c2603f22c2..d7f183c0026f752 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -828,7 +828,6 @@ internal_setblocking(PySocketSockObject *s, int block) int delay_flag, new_delay_flag; #endif - Py_BEGIN_ALLOW_THREADS #ifndef MS_WINDOWS #if (defined(HAVE_SYS_IOCTL_H) && defined(FIONBIO)) block = !block; @@ -855,8 +854,6 @@ internal_setblocking(PySocketSockObject *s, int block) result = 0; done: - Py_END_ALLOW_THREADS - if (result) { #ifndef MS_WINDOWS PyErr_SetFromErrno(PyExc_OSError); @@ -3915,9 +3912,7 @@ _socket_socket_getsockname_impl(PySocketSockObject *s) if (!getsockaddrlen(s, &addrlen)) return NULL; memset(&addrbuf, 0, addrlen); - Py_BEGIN_ALLOW_THREADS res = getsockname(get_sock_fd(s), SAS2SA(&addrbuf), &addrlen); - Py_END_ALLOW_THREADS if (res < 0) return s->errorhandler(); return makesockaddr(get_sock_fd(s), SAS2SA(&addrbuf), addrlen, @@ -3950,9 +3945,7 @@ _socket_socket_getpeername_impl(PySocketSockObject *s) if (!getsockaddrlen(s, &addrlen)) return NULL; memset(&addrbuf, 0, addrlen); - Py_BEGIN_ALLOW_THREADS res = getpeername(get_sock_fd(s), SAS2SA(&addrbuf), &addrlen); - Py_END_ALLOW_THREADS if (res < 0) return s->errorhandler(); return makesockaddr(get_sock_fd(s), SAS2SA(&addrbuf), addrlen, @@ -5968,9 +5961,7 @@ _socket_gethostname_impl(PyObject *module) #else char buf[1024]; int res; - Py_BEGIN_ALLOW_THREADS res = gethostname(buf, (int) sizeof buf - 1); - Py_END_ALLOW_THREADS if (res < 0) return set_error(); buf[sizeof buf - 1] = '\0'; From 1d1716434a5011fa0ca73b6eaf179473bb7e5c07 Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Tue, 29 Sep 2026 13:09:37 +0000 Subject: [PATCH 2/3] gh-89977: Revert epoll GIL changes --- Lib/test/test_epoll.py | 10 ---------- .../2026-09-23-09-21-25.gh-issue-89977.fmia18.rst | 4 ++-- Modules/selectmodule.c | 3 ++- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/Lib/test/test_epoll.py b/Lib/test/test_epoll.py index 97295fc98565eb5..51405ca7c9e57c3 100644 --- a/Lib/test/test_epoll.py +++ b/Lib/test/test_epoll.py @@ -238,16 +238,6 @@ def test_unregister_closed(self): ep.unregister(fd) self.assertEqual(cm.exception.errno, errno.EBADF) - def test_unregister_twice(self): - with select.epoll() as ep: - ep.register(self.serverSocket) - ep.unregister(self.serverSocket) - with self.assertRaises(OSError) as cm: - ep.unregister(self.serverSocket) - self.assertEqual(cm.exception.errno, errno.ENOENT) - ep.register(self.serverSocket) - ep.unregister(self.serverSocket) - def test_close(self): open_file = open(__file__, "rb") self.addCleanup(open_file.close) diff --git a/Misc/NEWS.d/next/Library/2026-09-23-09-21-25.gh-issue-89977.fmia18.rst b/Misc/NEWS.d/next/Library/2026-09-23-09-21-25.gh-issue-89977.fmia18.rst index 68704b779a4e10f..fcce4b6923dd3e6 100644 --- a/Misc/NEWS.d/next/Library/2026-09-23-09-21-25.gh-issue-89977.fmia18.rst +++ b/Misc/NEWS.d/next/Library/2026-09-23-09-21-25.gh-issue-89977.fmia18.rst @@ -1,3 +1,3 @@ Avoid releasing the GIL for :meth:`socket.socket.setblocking`, -:meth:`socket.socket.getsockname`, :meth:`socket.socket.getpeername`, -:func:`socket.gethostname`, and :meth:`select.epoll.unregister`. +:meth:`socket.socket.getsockname`, :meth:`socket.socket.getpeername`, and +:func:`socket.gethostname`. diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index 27de6f04d5d79f7..b9fb7762e3dacdf 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -1521,8 +1521,9 @@ pyepoll_internal_ctl(int epfd, int op, int fd, unsigned int events) /* In kernel versions before 2.6.9, the EPOLL_CTL_DEL * operation required a non-NULL pointer in event, even * though this argument is ignored. */ - /* Removing a watch is normally quick, so keep the GIL. */ + Py_BEGIN_ALLOW_THREADS result = epoll_ctl(epfd, op, fd, &ev); + Py_END_ALLOW_THREADS break; default: result = -1; From f097d7a3e8be0e521f435799ba730bb8a1ba658f Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Tue, 29 Sep 2026 15:43:37 +0000 Subject: [PATCH 3/3] gh-89977: Remove unrelated socket name test --- Lib/test/test_socket.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index c1434b4ac1f8b45..e4b3d848923f8c9 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -945,18 +945,6 @@ def requireSocket(*args): class GeneralModuleTests(unittest.TestCase): - def test_socket_name_errors(self): - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: - sock.bind((socket_helper.HOSTv4, 0)) - self.assertGreater(sock.getsockname()[1], 0) - with self.assertRaises(OSError): - sock.getpeername() - - with self.assertRaises(OSError): - sock.getsockname() - with self.assertRaises(OSError): - sock.getpeername() - @unittest.skipUnless(_socket is not None, 'need _socket module') def test_socket_type(self): self.assertTrue(gc.is_tracked(_socket.socket))