diff -r 464eed5ddb2e Doc/library/select.rst --- a/Doc/library/select.rst Mon Aug 19 10:03:11 2013 +0300 +++ b/Doc/library/select.rst Wed Aug 21 12:19:17 2013 +0200 @@ -147,6 +147,27 @@ descriptors), ``/dev/poll`` is O(active object. +.. method:: devpoll.close() + + Close the file descriptor of the polling object. + + .. versionadded:: 3.4 + + +.. attribute:: devpoll.closed + + ``True`` if the polling object is closed. + + .. versionadded:: 3.4 + + +.. method:: devpoll.fileno() + + Return the file descriptor number of the polling object. + + .. versionadded:: 3.4 + + .. method:: devpoll.register(fd[, eventmask]) Register a file descriptor with the polling object. Future calls to the @@ -244,6 +265,11 @@ Edge and Level Trigger Polling (epoll) O Close the control file descriptor of the epoll object. +.. attribute:: epoll.closed + + ``True`` if the epoll object is closed. + + .. method:: epoll.fileno() Return the file descriptor number of the control fd. @@ -363,6 +389,11 @@ Kqueue Objects Close the control file descriptor of the kqueue object. +.. attribute:: kqueue.closed + + ``True`` if the kqueue object is closed. + + .. method:: kqueue.fileno() Return the file descriptor number of the control fd. diff -r 464eed5ddb2e Lib/test/test_select.py --- a/Lib/test/test_select.py Mon Aug 19 10:03:11 2013 +0300 +++ b/Lib/test/test_select.py Wed Aug 21 12:19:17 2013 +0200 @@ -75,8 +75,55 @@ class SelectTestCase(unittest.TestCase): a[:] = [F()] * 10 self.assertEqual(select.select([], a, []), ([], a[:5], [])) + +class CloseTest(unittest.TestCase): + def setUp(self): + self.open_file = open(__file__, "rb") + self.addCleanup(self.open_file.close) + + def check_fileno_close(self, obj): + fd = obj.fileno() + self.assertIsInstance(fd, int) + self.assertFalse(obj.closed) + + obj.close() + self.assertTrue(obj.closed) + self.assertRaises(ValueError, obj.fileno) + + @unittest.skipUnless(hasattr(select, "devpoll"), "need select.devpoll") + def test_devpoll(self): + fd = self.open_file.fileno() + + obj = select.devpoll() + self.check_fileno_close(obj) + self.assertRaises(ValueError, obj.modify, fd) + self.assertRaises(ValueError, obj.poll, 1.0) + self.assertRaises(ValueError, obj.register, fd, fd, select.POLLIN) + self.assertRaises(ValueError, obj.unregister, fd) + + @unittest.skipUnless(hasattr(select, "epoll"), "need select.epoll") + def test_epoll(self): + fd = self.open_file.fileno() + obj = select.epoll() + + self.check_fileno_close(obj) + + self.assertRaises(ValueError, obj.modify, fd, select.EPOLLIN) + self.assertRaises(ValueError, obj.poll, 1.0) + self.assertRaises(ValueError, obj.register, fd, select.EPOLLIN) + self.assertRaises(ValueError, obj.unregister, fd) + + @unittest.skipUnless(hasattr(select, "kqueue"), "need select.kqueue") + def test_kqueue(self): + obj = select.kqueue() + self.check_fileno_close(obj) + + + def test_main(): - support.run_unittest(SelectTestCase) + support.run_unittest( + SelectTestCase, + CloseTest) support.reap_children() if __name__ == "__main__": diff -r 464eed5ddb2e Modules/selectmodule.c --- a/Modules/selectmodule.c Mon Aug 19 10:03:11 2013 +0300 +++ b/Modules/selectmodule.c Wed Aug 21 12:19:17 2013 +0200 @@ -671,6 +671,13 @@ typedef struct { static PyTypeObject devpoll_Type; +static PyObject * +devpoll_err_closed(void) +{ + PyErr_SetString(PyExc_ValueError, "I/O operation on closed devpoll object"); + return NULL; +} + static int devpoll_flush(devpollObject *self) { int size, n; @@ -711,6 +718,9 @@ internal_devpoll_register(devpollObject PyObject *o; int fd, events = POLLIN | POLLPRI | POLLOUT; + if (self->fd_devpoll < 0) + return devpoll_err_closed(); + if (!PyArg_ParseTuple(args, "O|i:register", &o, &events)) { return NULL; } @@ -775,6 +785,9 @@ devpoll_unregister(devpollObject *self, { int fd; + if (self->fd_devpoll < 0) + return devpoll_err_closed(); + fd = PyObject_AsFileDescriptor( o ); if (fd == -1) return NULL; @@ -804,6 +817,9 @@ devpoll_poll(devpollObject *self, PyObje long timeout; PyObject *value, *num1, *num2; + if (self->fd_devpoll < 0) + return devpoll_err_closed(); + if (!PyArg_UnpackTuple(args, "poll", 0, 1, &tout)) { return NULL; } @@ -882,6 +898,45 @@ devpoll_poll(devpollObject *self, PyObje return NULL; } +static PyObject* +devpoll_close(devpollObject *self) +{ + errno = devpoll_internal_close(self); + if (errno < 0) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(devpoll_close_doc, +"close() -> None\n\ +\n\ +Close the devpoll control file descriptor. Further operations on the devpoll\n\ +object will raise an exception."); + +static PyObject* +devpoll_get_closed(devpollObject *self) +{ + if (self->fd_devpoll < 0) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +static PyObject* +devpoll_fileno(devpollObject *self) +{ + if (self->fd_devpoll < 0) + return devpoll_err_closed(); + return PyLong_FromLong(self->fd_devpoll); +} + +PyDoc_STRVAR(devpoll_fileno_doc, +"fileno() -> int\n\ +\n\ +Return the file descriptor."); + static PyMethodDef devpoll_methods[] = { {"register", (PyCFunction)devpoll_register, METH_VARARGS, devpoll_register_doc}, @@ -891,9 +946,19 @@ static PyMethodDef devpoll_methods[] = { METH_O, devpoll_unregister_doc}, {"poll", (PyCFunction)devpoll_poll, METH_VARARGS, devpoll_poll_doc}, + {"close", (PyCFunction)devpoll_close, METH_NOARGS, + devpoll_close_doc}, + {"fileno", (PyCFunction)devpoll_fileno, METH_NOARGS, + devpoll_fileno_doc}, {NULL, NULL} /* sentinel */ }; +static PyGetSetDef devpoll_getsetlist[] = { + {"closed", (getter)devpoll_get_closed, NULL, + "True if the devpoll handler is closed"}, + {0}, +}; + static devpollObject * newDevPollObject(void) { @@ -944,15 +1009,26 @@ newDevPollObject(void) return self; } +static int +devpoll_internal_close(pyEpoll_Object *self) +{ + int save_errno = 0; + if (self->fd_devpoll >= 0) { + int fd = self->fd_devpoll; + self->fd_devpoll = -1; + Py_BEGIN_ALLOW_THREADS + if (close(fd) < 0) + save_errno = errno; + Py_END_ALLOW_THREADS + } + return save_errno; +} + static void devpoll_dealloc(devpollObject *self) { - Py_BEGIN_ALLOW_THREADS - close(self->fd_devpoll); - Py_END_ALLOW_THREADS - + (void)devpoll_internal_close(); PyMem_DEL(self->fds); - PyObject_Del(self); } @@ -988,6 +1064,8 @@ static PyTypeObject devpoll_Type = { 0, /*tp_iter*/ 0, /*tp_iternext*/ devpoll_methods, /*tp_methods*/ + 0, /* tp_members */ + devpoll_getsetlist, /* tp_getset */ }; #endif /* HAVE_SYS_DEVPOLL_H */ @@ -1071,7 +1149,7 @@ static PyTypeObject pyEpoll_Type; static PyObject * pyepoll_err_closed(void) { - PyErr_SetString(PyExc_ValueError, "I/O operation on closed epoll fd"); + PyErr_SetString(PyExc_ValueError, "I/O operation on closed epoll object"); return NULL; } @@ -1763,7 +1841,7 @@ static PyTypeObject kqueue_event_Type = static PyObject * kqueue_queue_err_closed(void) { - PyErr_SetString(PyExc_ValueError, "I/O operation on closed kqueue fd"); + PyErr_SetString(PyExc_ValueError, "I/O operation on closed kqueue object"); return NULL; }