--- /tmp/HPZHwC_selectors.py 2016-08-13 17:24:29.416754000 -0700 +++ Lib/selectors.py 2016-08-13 16:56:17.641027000 -0700 @@ -408,8 +408,13 @@ epoll_events |= select.EPOLLIN if events & EVENT_WRITE: epoll_events |= select.EPOLLOUT - self._epoll.register(key.fd, epoll_events) - return key + try: + self._epoll.register(key.fd, epoll_events) + except BaseException: + super().unregister(fileobj) + raise + else: + return key def unregister(self, fileobj): key = super().unregister(fileobj) @@ -530,15 +535,20 @@ def register(self, fileobj, events, data=None): key = super().register(fileobj, events, data) - if events & EVENT_READ: - kev = select.kevent(key.fd, select.KQ_FILTER_READ, - select.KQ_EV_ADD) - self._kqueue.control([kev], 0, 0) - if events & EVENT_WRITE: - kev = select.kevent(key.fd, select.KQ_FILTER_WRITE, - select.KQ_EV_ADD) - self._kqueue.control([kev], 0, 0) - return key + try: + if events & EVENT_READ: + kev = select.kevent(key.fd, select.KQ_FILTER_READ, + select.KQ_EV_ADD) + self._kqueue.control([kev], 0, 0) + if events & EVENT_WRITE: + kev = select.kevent(key.fd, select.KQ_FILTER_WRITE, + select.KQ_EV_ADD) + self._kqueue.control([kev], 0, 0) + except BaseException: + super().unregister(fileobj) + raise + else: + return key def unregister(self, fileobj): key = super().unregister(fileobj) --- /tmp/Xb3zXF_test_selectors.py 2016-08-13 17:24:29.434206000 -0700 +++ Lib/test/test_selectors.py 2016-08-13 16:57:37.891466000 -0700 @@ -9,6 +9,7 @@ from time import sleep import unittest import unittest.mock +import tempfile from time import monotonic as time try: import resource @@ -475,6 +476,16 @@ SELECTOR = getattr(selectors, 'EpollSelector', None) + def test_register_file(self): + # epoll(7) returns EPERM when given a file to watch + s = self.SELECTOR() + with tempfile.NamedTemporaryFile() as f: + with self.assertRaises(IOError): + s.register(f, selectors.EVENT_READ) + # the SelectorKey has been removed + with self.assertRaises(KeyError): + s.get_key(f) + @unittest.skipUnless(hasattr(selectors, 'KqueueSelector'), "Test needs selectors.KqueueSelector)") @@ -482,6 +493,20 @@ SELECTOR = getattr(selectors, 'KqueueSelector', None) + def test_register_bad_fd(self): + # a file descriptor that's been closed should raise an OSError + # with EBADF + s = self.SELECTOR() + pipe_read, pipe_write = os.pipe() + os.close(pipe_read) + os.close(pipe_write) + with self.assertRaises(OSError) as cm: + s.register(pipe_read, selectors.EVENT_READ) + self.assertEqual(cm.exception.errno, errno.EBADF) + # the SelectorKey has been removed + with self.assertRaises(KeyError): + s.get_key(pipe_read) + @unittest.skipUnless(hasattr(selectors, 'DevpollSelector'), "Test needs selectors.DevpollSelector")