diff -r 8a28c974f903 Modules/selectmodule.c --- a/Modules/selectmodule.c Sun Nov 11 19:40:38 2012 +0100 +++ b/Modules/selectmodule.c Sun Nov 11 13:47:31 2012 -0800 @@ -312,8 +312,16 @@ typedef struct { PyObject_HEAD PyObject *dict; - int ufd_uptodate; - int ufd_len; + /* Used to determine when dict has been mutated and the ufds array + * needs to be updated. This is also used to prevent sharing the + * ufds array across multiple threads using the same polling object. + * tag is incremented any time dict is modified. ufd_tag is set + * to the tag version of the dict it represents if ufds != NULL. */ + unsigned long long tag; + unsigned long long ufd_tag; + /* A cache of dict in the form of a pollfd array to avoid + * recreating it from the same dict on every call to poll(). */ + Py_ssize_t ufd_len; struct pollfd *ufds; } pollObject; @@ -330,6 +338,8 @@ PyObject *key, *value; struct pollfd *old_ufds = self->ufds; + assert(self->ufds == NULL || self->ufd_tag != self->tag); + self->ufd_len = PyDict_Size(self->dict); PyMem_RESIZE(self->ufds, struct pollfd, self->ufd_len); if (self->ufds == NULL) { @@ -344,7 +354,7 @@ self->ufds[i].events = (short)PyLong_AsLong(value); i++; } - self->ufd_uptodate = 1; + self->ufd_tag = self->tag; return 1; } @@ -385,10 +395,9 @@ if (err < 0) return NULL; - self->ufd_uptodate = 0; + self->tag += 1; - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } PyDoc_STRVAR(poll_modify_doc, @@ -433,10 +442,9 @@ if (err < 0) return NULL; - self->ufd_uptodate = 0; + self->tag += 1; - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } @@ -467,12 +475,22 @@ } Py_DECREF(key); - self->ufd_uptodate = 0; + self->tag += 1; - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } +/* Restore ufds in the struct if no other thread has touched it (same tag); + * otherwise we've used this one up and a new one exists, free ours. */ +#define CLEANUP_UFDS \ + if (self->ufds == NULL && self->tag == ufd_tag) { \ + self->ufds = ufds; \ + self->ufd_tag = ufd_tag; \ + self->ufd_len = ufd_len; \ + } else { \ + PyMem_FREE(ufds); \ + } + PyDoc_STRVAR(poll_poll_doc, "poll( [timeout] ) -> list of (fd, event) 2-tuples\n\n\ Polls the set of registered file descriptors, returning a list containing \n\ @@ -482,8 +500,9 @@ poll_poll(pollObject *self, PyObject *args) { PyObject *result_list = NULL, *tout = NULL; - int timeout = 0, poll_result, i, j; + int timeout = 0, poll_result, i, j, ufd_tag, ufd_len; PyObject *value = NULL, *num = NULL; + struct pollfd *ufds; if (!PyArg_UnpackTuple(args, "poll", 0, 1, &tout)) { return NULL; @@ -508,17 +527,25 @@ } /* Ensure the ufd array is up to date */ - if (!self->ufd_uptodate) + if (!self->ufds || (self->ufd_tag != self->tag)) if (update_ufd_array(self) == 0) return NULL; + /* Take ownership of this ufds array to ensure it is not modified + * by other threads while the GIL is released during poll(). */ + ufd_tag = self->ufd_tag; + ufd_len = self->ufd_len; + ufds = self->ufds; + self->ufds = NULL; + /* call poll() */ Py_BEGIN_ALLOW_THREADS - poll_result = poll(self->ufds, self->ufd_len, timeout); + poll_result = poll(ufds, ufd_len, timeout); Py_END_ALLOW_THREADS if (poll_result < 0) { PyErr_SetFromErrno(SelectError); + CLEANUP_UFDS return NULL; } @@ -526,11 +553,11 @@ result_list = PyList_New(poll_result); if (!result_list) - return NULL; + goto error; else { for (i = 0, j = 0; j < poll_result; j++) { /* skip to the next fired descriptor */ - while (!self->ufds[i].revents) { + while (!ufds[i].revents) { i++; } /* if we hit a NULL return, set value to NULL @@ -539,7 +566,7 @@ value = PyTuple_New(2); if (value == NULL) goto error; - num = PyLong_FromLong(self->ufds[i].fd); + num = PyLong_FromLong(ufds[i].fd); if (num == NULL) { Py_DECREF(value); goto error; @@ -550,7 +577,7 @@ is a 16-bit short, and IBM assigned POLLNVAL to be 0x8000, so the conversion to int results in a negative number. See SF bug #923315. */ - num = PyLong_FromLong(self->ufds[i].revents & 0xffff); + num = PyLong_FromLong(ufds[i].revents & 0xffff); if (num == NULL) { Py_DECREF(value); goto error; @@ -563,11 +590,14 @@ i++; } } - return result_list; + goto normal_exit; error: - Py_DECREF(result_list); - return NULL; + Py_CLEAR(result_list); + + normal_exit: + CLEANUP_UFDS + return result_list; } static PyMethodDef poll_methods[] = { @@ -589,9 +619,10 @@ self = PyObject_New(pollObject, &poll_Type); if (self == NULL) return NULL; - /* ufd_uptodate is a Boolean, denoting whether the - array pointed to by ufds matches the contents of the dictionary. */ - self->ufd_uptodate = 0; + /* tag is incremented every time the dictionary is updated; ufd_tag is + * the tag that currently belong to the ufds array. + * if (ufds != NULL && tag == ufd_tag) then ufds is up-to-date */ + self->tag = 0; self->ufds = NULL; self->dict = PyDict_New(); if (self->dict == NULL) {