gh-121592: Make select.poll() and related objects thread-safe (#121594)

This makes select.poll() and kqueue() objects thread-safe in the
free-threaded build. Note that calling close() concurrently with other
functions is still not thread-safe due to races on file descriptors
(gh-121544).
This commit is contained in:
Sam Gross 2024-07-11 10:21:09 -04:00 committed by GitHub
parent e6264b44dc
commit 44937d11a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 96 additions and 22 deletions

View File

@ -6,6 +6,7 @@ preserve
# include "pycore_gc.h" // PyGC_Head
# include "pycore_runtime.h" // _Py_ID()
#endif
#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
#include "pycore_long.h" // _PyLong_UnsignedShort_Converter()
#include "pycore_modsupport.h" // _PyArg_CheckPositional()
@ -110,7 +111,9 @@ select_poll_register(pollObject *self, PyObject *const *args, Py_ssize_t nargs)
goto exit;
}
skip_optional:
Py_BEGIN_CRITICAL_SECTION(self);
return_value = select_poll_register_impl(self, fd, eventmask);
Py_END_CRITICAL_SECTION();
exit:
return return_value;
@ -155,7 +158,9 @@ select_poll_modify(pollObject *self, PyObject *const *args, Py_ssize_t nargs)
if (!_PyLong_UnsignedShort_Converter(args[1], &eventmask)) {
goto exit;
}
Py_BEGIN_CRITICAL_SECTION(self);
return_value = select_poll_modify_impl(self, fd, eventmask);
Py_END_CRITICAL_SECTION();
exit:
return return_value;
@ -187,7 +192,9 @@ select_poll_unregister(pollObject *self, PyObject *arg)
if (fd < 0) {
goto exit;
}
Py_BEGIN_CRITICAL_SECTION(self);
return_value = select_poll_unregister_impl(self, fd);
Py_END_CRITICAL_SECTION();
exit:
return return_value;
@ -230,7 +237,9 @@ select_poll_poll(pollObject *self, PyObject *const *args, Py_ssize_t nargs)
}
timeout_obj = args[0];
skip_optional:
Py_BEGIN_CRITICAL_SECTION(self);
return_value = select_poll_poll_impl(self, timeout_obj);
Py_END_CRITICAL_SECTION();
exit:
return return_value;
@ -281,7 +290,9 @@ select_devpoll_register(devpollObject *self, PyObject *const *args, Py_ssize_t n
goto exit;
}
skip_optional:
Py_BEGIN_CRITICAL_SECTION(self);
return_value = select_devpoll_register_impl(self, fd, eventmask);
Py_END_CRITICAL_SECTION();
exit:
return return_value;
@ -332,7 +343,9 @@ select_devpoll_modify(devpollObject *self, PyObject *const *args, Py_ssize_t nar
goto exit;
}
skip_optional:
Py_BEGIN_CRITICAL_SECTION(self);
return_value = select_devpoll_modify_impl(self, fd, eventmask);
Py_END_CRITICAL_SECTION();
exit:
return return_value;
@ -364,7 +377,9 @@ select_devpoll_unregister(devpollObject *self, PyObject *arg)
if (fd < 0) {
goto exit;
}
Py_BEGIN_CRITICAL_SECTION(self);
return_value = select_devpoll_unregister_impl(self, fd);
Py_END_CRITICAL_SECTION();
exit:
return return_value;
@ -407,7 +422,9 @@ select_devpoll_poll(devpollObject *self, PyObject *const *args, Py_ssize_t nargs
}
timeout_obj = args[0];
skip_optional:
Py_BEGIN_CRITICAL_SECTION(self);
return_value = select_devpoll_poll_impl(self, timeout_obj);
Py_END_CRITICAL_SECTION();
exit:
return return_value;
@ -434,7 +451,13 @@ select_devpoll_close_impl(devpollObject *self);
static PyObject *
select_devpoll_close(devpollObject *self, PyObject *Py_UNUSED(ignored))
{
return select_devpoll_close_impl(self);
PyObject *return_value = NULL;
Py_BEGIN_CRITICAL_SECTION(self);
return_value = select_devpoll_close_impl(self);
Py_END_CRITICAL_SECTION();
return return_value;
}
#endif /* (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) */
@ -456,7 +479,13 @@ select_devpoll_fileno_impl(devpollObject *self);
static PyObject *
select_devpoll_fileno(devpollObject *self, PyObject *Py_UNUSED(ignored))
{
return select_devpoll_fileno_impl(self);
PyObject *return_value = NULL;
Py_BEGIN_CRITICAL_SECTION(self);
return_value = select_devpoll_fileno_impl(self);
Py_END_CRITICAL_SECTION();
return return_value;
}
#endif /* (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) */
@ -615,7 +644,13 @@ select_epoll_close_impl(pyEpoll_Object *self);
static PyObject *
select_epoll_close(pyEpoll_Object *self, PyObject *Py_UNUSED(ignored))
{
return select_epoll_close_impl(self);
PyObject *return_value = NULL;
Py_BEGIN_CRITICAL_SECTION(self);
return_value = select_epoll_close_impl(self);
Py_END_CRITICAL_SECTION();
return return_value;
}
#endif /* defined(HAVE_EPOLL) */
@ -1108,7 +1143,13 @@ select_kqueue_close_impl(kqueue_queue_Object *self);
static PyObject *
select_kqueue_close(kqueue_queue_Object *self, PyObject *Py_UNUSED(ignored))
{
return select_kqueue_close_impl(self);
PyObject *return_value = NULL;
Py_BEGIN_CRITICAL_SECTION(self);
return_value = select_kqueue_close_impl(self);
Py_END_CRITICAL_SECTION();
return return_value;
}
#endif /* defined(HAVE_KQUEUE) */
@ -1319,4 +1360,4 @@ exit:
#ifndef SELECT_KQUEUE_CONTROL_METHODDEF
#define SELECT_KQUEUE_CONTROL_METHODDEF
#endif /* !defined(SELECT_KQUEUE_CONTROL_METHODDEF) */
/*[clinic end generated code: output=4fc17ae9b6cfdc86 input=a9049054013a1b77]*/
/*[clinic end generated code: output=f31e724f492225b1 input=a9049054013a1b77]*/

View File

@ -473,6 +473,7 @@ update_ufd_array(pollObject *self)
}
/*[clinic input]
@critical_section
select.poll.register
fd: fildes
@ -486,7 +487,7 @@ Register a file descriptor with the polling object.
static PyObject *
select_poll_register_impl(pollObject *self, int fd, unsigned short eventmask)
/*[clinic end generated code: output=0dc7173c800a4a65 input=34e16cfb28d3c900]*/
/*[clinic end generated code: output=0dc7173c800a4a65 input=c475e029ce6c2830]*/
{
PyObject *key, *value;
int err;
@ -514,6 +515,7 @@ select_poll_register_impl(pollObject *self, int fd, unsigned short eventmask)
/*[clinic input]
@critical_section
select.poll.modify
fd: fildes
@ -528,7 +530,7 @@ Modify an already registered file descriptor.
static PyObject *
select_poll_modify_impl(pollObject *self, int fd, unsigned short eventmask)
/*[clinic end generated code: output=1a7b88bf079eff17 input=a8e383df075c32cf]*/
/*[clinic end generated code: output=1a7b88bf079eff17 input=38c9db5346711872]*/
{
PyObject *key, *value;
int err;
@ -566,6 +568,7 @@ select_poll_modify_impl(pollObject *self, int fd, unsigned short eventmask)
/*[clinic input]
@critical_section
select.poll.unregister
fd: fildes
@ -576,7 +579,7 @@ Remove a file descriptor being tracked by the polling object.
static PyObject *
select_poll_unregister_impl(pollObject *self, int fd)
/*[clinic end generated code: output=8c9f42e75e7d291b input=4b4fccc1040e79cb]*/
/*[clinic end generated code: output=8c9f42e75e7d291b input=ae6315d7f5243704]*/
{
PyObject *key;
@ -599,6 +602,7 @@ select_poll_unregister_impl(pollObject *self, int fd)
}
/*[clinic input]
@critical_section
select.poll.poll
timeout as timeout_obj: object = None
@ -614,7 +618,7 @@ report, as a list of (fd, event) 2-tuples.
static PyObject *
select_poll_poll_impl(pollObject *self, PyObject *timeout_obj)
/*[clinic end generated code: output=876e837d193ed7e4 input=c2f6953ec45e5622]*/
/*[clinic end generated code: output=876e837d193ed7e4 input=54310631457efdec]*/
{
PyObject *result_list = NULL;
int poll_result, i, j;
@ -857,6 +861,7 @@ internal_devpoll_register(devpollObject *self, int fd,
}
/*[clinic input]
@critical_section
select.devpoll.register
fd: fildes
@ -872,12 +877,13 @@ Register a file descriptor with the polling object.
static PyObject *
select_devpoll_register_impl(devpollObject *self, int fd,
unsigned short eventmask)
/*[clinic end generated code: output=6e07fe8b74abba0c input=22006fabe9567522]*/
/*[clinic end generated code: output=6e07fe8b74abba0c input=8d48bd2653a61c42]*/
{
return internal_devpoll_register(self, fd, eventmask, 0);
}
/*[clinic input]
@critical_section
select.devpoll.modify
fd: fildes
@ -893,12 +899,13 @@ Modify a possible already registered file descriptor.
static PyObject *
select_devpoll_modify_impl(devpollObject *self, int fd,
unsigned short eventmask)
/*[clinic end generated code: output=bc2e6d23aaff98b4 input=09fa335db7cdc09e]*/
/*[clinic end generated code: output=bc2e6d23aaff98b4 input=773b37e9abca2460]*/
{
return internal_devpoll_register(self, fd, eventmask, 1);
}
/*[clinic input]
@critical_section
select.devpoll.unregister
fd: fildes
@ -909,7 +916,7 @@ Remove a file descriptor being tracked by the polling object.
static PyObject *
select_devpoll_unregister_impl(devpollObject *self, int fd)
/*[clinic end generated code: output=95519ffa0c7d43fe input=b4ea42a4442fd467]*/
/*[clinic end generated code: output=95519ffa0c7d43fe input=6052d368368d4d05]*/
{
if (self->fd_devpoll < 0)
return devpoll_err_closed();
@ -926,6 +933,7 @@ select_devpoll_unregister_impl(devpollObject *self, int fd)
}
/*[clinic input]
@critical_section
select.devpoll.poll
timeout as timeout_obj: object = None
The maximum time to wait in milliseconds, or else None (or a negative
@ -940,7 +948,7 @@ report, as a list of (fd, event) 2-tuples.
static PyObject *
select_devpoll_poll_impl(devpollObject *self, PyObject *timeout_obj)
/*[clinic end generated code: output=2654e5457cca0b3c input=3c3f0a355ec2bedb]*/
/*[clinic end generated code: output=2654e5457cca0b3c input=fe7a3f6dcbc118c5]*/
{
struct dvpoll dvp;
PyObject *result_list = NULL;
@ -1059,6 +1067,7 @@ devpoll_internal_close(devpollObject *self)
}
/*[clinic input]
@critical_section
select.devpoll.close
Close the devpoll file descriptor.
@ -1068,7 +1077,7 @@ Further operations on the devpoll object will raise an exception.
static PyObject *
select_devpoll_close_impl(devpollObject *self)
/*[clinic end generated code: output=26b355bd6429f21b input=6273c30f5560a99b]*/
/*[clinic end generated code: output=26b355bd6429f21b input=408fde21a377ccfb]*/
{
errno = devpoll_internal_close(self);
if (errno < 0) {
@ -1088,6 +1097,7 @@ devpoll_get_closed(devpollObject *self, void *Py_UNUSED(ignored))
}
/*[clinic input]
@critical_section
select.devpoll.fileno
Return the file descriptor.
@ -1095,7 +1105,7 @@ Return the file descriptor.
static PyObject *
select_devpoll_fileno_impl(devpollObject *self)
/*[clinic end generated code: output=26920929f8d292f4 input=ef15331ebde6c368]*/
/*[clinic end generated code: output=26920929f8d292f4 input=8c9db2efa1ade538]*/
{
if (self->fd_devpoll < 0)
return devpoll_err_closed();
@ -1378,6 +1388,7 @@ pyepoll_dealloc(pyEpoll_Object *self)
}
/*[clinic input]
@critical_section
select.epoll.close
Close the epoll control file descriptor.
@ -1387,7 +1398,7 @@ Further operations on the epoll object will raise an exception.
static PyObject *
select_epoll_close_impl(pyEpoll_Object *self)
/*[clinic end generated code: output=ee2144c446a1a435 input=ca6c66ba5a736bfd]*/
/*[clinic end generated code: output=ee2144c446a1a435 input=f626a769192e1dbe]*/
{
errno = pyepoll_internal_close(self);
if (errno < 0) {
@ -2023,10 +2034,8 @@ finally:
}
static int
kqueue_tracking_add(_selectstate *state, kqueue_queue_Object *self) {
if (!state->kqueue_tracking_initialized) {
kqueue_tracking_init(PyType_GetModule(Py_TYPE(self)));
}
kqueue_tracking_add_lock_held(_selectstate *state, kqueue_queue_Object *self)
{
assert(self->kqfd >= 0);
_kqueue_list_item *item = PyMem_New(_kqueue_list_item, 1);
if (item == NULL) {
@ -2039,8 +2048,23 @@ kqueue_tracking_add(_selectstate *state, kqueue_queue_Object *self) {
return 0;
}
static int
kqueue_tracking_add(_selectstate *state, kqueue_queue_Object *self)
{
int ret;
PyObject *module = PyType_GetModule(Py_TYPE(self));
Py_BEGIN_CRITICAL_SECTION(module);
if (!state->kqueue_tracking_initialized) {
kqueue_tracking_init(module);
}
ret = kqueue_tracking_add_lock_held(state, self);
Py_END_CRITICAL_SECTION();
return ret;
}
static void
kqueue_tracking_remove(_selectstate *state, kqueue_queue_Object *self) {
kqueue_tracking_remove_lock_held(_selectstate *state, kqueue_queue_Object *self)
{
_kqueue_list *listptr = &state->kqueue_open_list;
while (*listptr != NULL) {
_kqueue_list_item *item = *listptr;
@ -2056,6 +2080,14 @@ kqueue_tracking_remove(_selectstate *state, kqueue_queue_Object *self) {
assert(0);
}
static void
kqueue_tracking_remove(_selectstate *state, kqueue_queue_Object *self)
{
Py_BEGIN_CRITICAL_SECTION(PyType_GetModule(Py_TYPE(self)));
kqueue_tracking_remove_lock_held(state, self);
Py_END_CRITICAL_SECTION();
}
static int
kqueue_queue_internal_close(kqueue_queue_Object *self)
{
@ -2150,6 +2182,7 @@ kqueue_queue_finalize(kqueue_queue_Object *self)
}
/*[clinic input]
@critical_section
select.kqueue.close
Close the kqueue control file descriptor.
@ -2159,7 +2192,7 @@ Further operations on the kqueue object will raise an exception.
static PyObject *
select_kqueue_close_impl(kqueue_queue_Object *self)
/*[clinic end generated code: output=d1c7df0b407a4bc1 input=0b12d95430e0634c]*/
/*[clinic end generated code: output=d1c7df0b407a4bc1 input=6d763c858b17b690]*/
{
errno = kqueue_queue_internal_close(self);
if (errno < 0) {