Skip to content
3 changes: 1 addition & 2 deletions Lib/test/test_selectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -603,8 +603,7 @@ def test_empty_select_timeout(self):

@unittest.skipUnless(hasattr(selectors, 'DevpollSelector'),
"Test needs selectors.DevpollSelector")
class DevpollSelectorTestCase(BaseSelectorTestCase, ScalableSelectorMixIn,
unittest.TestCase):
class DevpollSelectorTestCase(BaseSelectorTestCase, unittest.TestCase):

SELECTOR = getattr(selectors, 'DevpollSelector', None)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed excessive memory allocation and data race issues in Solaris specific ``DevPollSelector``. Patch by Jakub Kulik.
50 changes: 37 additions & 13 deletions Modules/selectmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -824,12 +824,16 @@ poll_dealloc(PyObject *op)
#ifdef HAVE_SYS_DEVPOLL_H
static PyMethodDef devpoll_methods[];

#define DEVPOLL_IN_BUFFER_SIZE 128
#define DEVPOLL_MAX_OUT_BUFFER_SIZE 1024

typedef struct {
PyObject_HEAD
int fd_devpoll;
int max_n_fds;
int n_fds;
int out_size;
struct pollfd *fds;
struct pollfd *out_fds;
} devpollObject;

#define devpollObject_CAST(op) ((devpollObject *)(op))
Expand Down Expand Up @@ -882,7 +886,7 @@ internal_devpoll_register(devpollObject *self, int fd,
self->fds[self->n_fds].fd = fd;
self->fds[self->n_fds].events = POLLREMOVE;

if (++self->n_fds == self->max_n_fds) {
if (++self->n_fds == DEVPOLL_IN_BUFFER_SIZE) {
if (devpoll_flush(self))
return NULL;
}
Expand All @@ -891,7 +895,7 @@ internal_devpoll_register(devpollObject *self, int fd,
self->fds[self->n_fds].fd = fd;
self->fds[self->n_fds].events = (signed short)events;

if (++self->n_fds == self->max_n_fds) {
if (++self->n_fds == DEVPOLL_IN_BUFFER_SIZE) {
if (devpoll_flush(self))
return NULL;
}
Expand Down Expand Up @@ -963,7 +967,7 @@ select_devpoll_unregister_impl(devpollObject *self, int fd)
self->fds[self->n_fds].fd = fd;
self->fds[self->n_fds].events = POLLREMOVE;

if (++self->n_fds == self->max_n_fds) {
if (++self->n_fds == DEVPOLL_IN_BUFFER_SIZE) {
if (devpoll_flush(self))
return NULL;
}
Expand Down Expand Up @@ -1024,8 +1028,8 @@ select_devpoll_poll_impl(devpollObject *self, PyObject *timeout_obj)
if (devpoll_flush(self))
return NULL;

dvp.dp_fds = self->fds;
dvp.dp_nfds = self->max_n_fds;
dvp.dp_fds = self->out_fds;
dvp.dp_nfds = self->out_size;
dvp.dp_timeout = (int)ms;

if (timeout >= 0) {
Expand Down Expand Up @@ -1069,8 +1073,8 @@ select_devpoll_poll_impl(devpollObject *self, PyObject *timeout_obj)
return NULL;

for (i = 0; i < poll_result; i++) {
num1 = PyLong_FromLong(self->fds[i].fd);
num2 = PyLong_FromLong(self->fds[i].revents);
num1 = PyLong_FromLong(self->out_fds[i].fd);
num2 = PyLong_FromLong(self->out_fds[i].revents);
if ((num1 == NULL) || (num2 == NULL)) {
Py_XDECREF(num1);
Py_XDECREF(num2);
Expand Down Expand Up @@ -1162,12 +1166,12 @@ static devpollObject *
newDevPollObject(PyObject *module)
{
devpollObject *self;
int fd_devpoll, limit_result;
struct pollfd *fds;
int fd_devpoll, limit_result, out_size;
struct pollfd *fds, *out_fds;
struct rlimit limit;

/*
** If we try to process more that getrlimit()
** If we try to process more than getrlimit()
** fds, the kernel will give an error, so
** we set the limit here. It is a dynamic
** value, because we can change rlimit() anytime.
Expand All @@ -1178,27 +1182,46 @@ newDevPollObject(PyObject *module)
return NULL;
}

/*
** If the limit is too high (or RLIM_INFINITY), we might
** allocate huge amounts of memory or even fail to allocate.
*/
out_size = limit.rlim_cur;
if ((rlim_t)out_size > DEVPOLL_MAX_OUT_BUFFER_SIZE) {
out_size = DEVPOLL_MAX_OUT_BUFFER_SIZE;
}

fd_devpoll = _Py_open("/dev/poll", O_RDWR);
if (fd_devpoll == -1)
return NULL;

fds = PyMem_NEW(struct pollfd, limit.rlim_cur);
fds = PyMem_NEW(struct pollfd, DEVPOLL_IN_BUFFER_SIZE);
if (fds == NULL) {
close(fd_devpoll);
PyErr_NoMemory();
return NULL;
}

out_fds = PyMem_NEW(struct pollfd, out_size);
if (fds == NULL) {
close(fd_devpoll);
PyMem_Free(fds);
PyErr_NoMemory();
return NULL;
}

self = PyObject_New(devpollObject, get_select_state(module)->devpoll_Type);
if (self == NULL) {
close(fd_devpoll);
PyMem_Free(fds);
PyMem_Free(out_fds);
return NULL;
}
self->fd_devpoll = fd_devpoll;
self->max_n_fds = limit.rlim_cur;
self->n_fds = 0;
self->fds = fds;
self->out_size = out_size;
self->out_fds = out_fds;

return self;
}
Expand All @@ -1210,6 +1233,7 @@ devpoll_dealloc(PyObject *op)
PyTypeObject *type = Py_TYPE(self);
(void)devpoll_internal_close(self);
PyMem_Free(self->fds);
PyMem_Free(self->out_fds);
PyObject_Free(self);
Py_DECREF(type);
}
Expand Down
Loading