test_selectors.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. import errno
  2. import os
  3. import random
  4. import selectors
  5. import signal
  6. import socket
  7. import sys
  8. from test import support
  9. from test.support import os_helper
  10. from test.support import socket_helper
  11. from time import sleep
  12. import unittest
  13. import unittest.mock
  14. import tempfile
  15. from time import monotonic as time
  16. try:
  17. import resource
  18. except ImportError:
  19. resource = None
  20. if support.is_emscripten or support.is_wasi:
  21. raise unittest.SkipTest("Cannot create socketpair on Emscripten/WASI.")
  22. if hasattr(socket, 'socketpair'):
  23. socketpair = socket.socketpair
  24. else:
  25. def socketpair(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0):
  26. with socket.socket(family, type, proto) as l:
  27. l.bind((socket_helper.HOST, 0))
  28. l.listen()
  29. c = socket.socket(family, type, proto)
  30. try:
  31. c.connect(l.getsockname())
  32. caddr = c.getsockname()
  33. while True:
  34. a, addr = l.accept()
  35. # check that we've got the correct client
  36. if addr == caddr:
  37. return c, a
  38. a.close()
  39. except OSError:
  40. c.close()
  41. raise
  42. def find_ready_matching(ready, flag):
  43. match = []
  44. for key, events in ready:
  45. if events & flag:
  46. match.append(key.fileobj)
  47. return match
  48. class BaseSelectorTestCase:
  49. def make_socketpair(self):
  50. rd, wr = socketpair()
  51. self.addCleanup(rd.close)
  52. self.addCleanup(wr.close)
  53. return rd, wr
  54. def test_register(self):
  55. s = self.SELECTOR()
  56. self.addCleanup(s.close)
  57. rd, wr = self.make_socketpair()
  58. key = s.register(rd, selectors.EVENT_READ, "data")
  59. self.assertIsInstance(key, selectors.SelectorKey)
  60. self.assertEqual(key.fileobj, rd)
  61. self.assertEqual(key.fd, rd.fileno())
  62. self.assertEqual(key.events, selectors.EVENT_READ)
  63. self.assertEqual(key.data, "data")
  64. # register an unknown event
  65. self.assertRaises(ValueError, s.register, 0, 999999)
  66. # register an invalid FD
  67. self.assertRaises(ValueError, s.register, -10, selectors.EVENT_READ)
  68. # register twice
  69. self.assertRaises(KeyError, s.register, rd, selectors.EVENT_READ)
  70. # register the same FD, but with a different object
  71. self.assertRaises(KeyError, s.register, rd.fileno(),
  72. selectors.EVENT_READ)
  73. def test_unregister(self):
  74. s = self.SELECTOR()
  75. self.addCleanup(s.close)
  76. rd, wr = self.make_socketpair()
  77. s.register(rd, selectors.EVENT_READ)
  78. s.unregister(rd)
  79. # unregister an unknown file obj
  80. self.assertRaises(KeyError, s.unregister, 999999)
  81. # unregister twice
  82. self.assertRaises(KeyError, s.unregister, rd)
  83. def test_unregister_after_fd_close(self):
  84. s = self.SELECTOR()
  85. self.addCleanup(s.close)
  86. rd, wr = self.make_socketpair()
  87. r, w = rd.fileno(), wr.fileno()
  88. s.register(r, selectors.EVENT_READ)
  89. s.register(w, selectors.EVENT_WRITE)
  90. rd.close()
  91. wr.close()
  92. s.unregister(r)
  93. s.unregister(w)
  94. @unittest.skipUnless(os.name == 'posix', "requires posix")
  95. def test_unregister_after_fd_close_and_reuse(self):
  96. s = self.SELECTOR()
  97. self.addCleanup(s.close)
  98. rd, wr = self.make_socketpair()
  99. r, w = rd.fileno(), wr.fileno()
  100. s.register(r, selectors.EVENT_READ)
  101. s.register(w, selectors.EVENT_WRITE)
  102. rd2, wr2 = self.make_socketpair()
  103. rd.close()
  104. wr.close()
  105. os.dup2(rd2.fileno(), r)
  106. os.dup2(wr2.fileno(), w)
  107. self.addCleanup(os.close, r)
  108. self.addCleanup(os.close, w)
  109. s.unregister(r)
  110. s.unregister(w)
  111. def test_unregister_after_socket_close(self):
  112. s = self.SELECTOR()
  113. self.addCleanup(s.close)
  114. rd, wr = self.make_socketpair()
  115. s.register(rd, selectors.EVENT_READ)
  116. s.register(wr, selectors.EVENT_WRITE)
  117. rd.close()
  118. wr.close()
  119. s.unregister(rd)
  120. s.unregister(wr)
  121. def test_modify(self):
  122. s = self.SELECTOR()
  123. self.addCleanup(s.close)
  124. rd, wr = self.make_socketpair()
  125. key = s.register(rd, selectors.EVENT_READ)
  126. # modify events
  127. key2 = s.modify(rd, selectors.EVENT_WRITE)
  128. self.assertNotEqual(key.events, key2.events)
  129. self.assertEqual(key2, s.get_key(rd))
  130. s.unregister(rd)
  131. # modify data
  132. d1 = object()
  133. d2 = object()
  134. key = s.register(rd, selectors.EVENT_READ, d1)
  135. key2 = s.modify(rd, selectors.EVENT_READ, d2)
  136. self.assertEqual(key.events, key2.events)
  137. self.assertNotEqual(key.data, key2.data)
  138. self.assertEqual(key2, s.get_key(rd))
  139. self.assertEqual(key2.data, d2)
  140. # modify unknown file obj
  141. self.assertRaises(KeyError, s.modify, 999999, selectors.EVENT_READ)
  142. # modify use a shortcut
  143. d3 = object()
  144. s.register = unittest.mock.Mock()
  145. s.unregister = unittest.mock.Mock()
  146. s.modify(rd, selectors.EVENT_READ, d3)
  147. self.assertFalse(s.register.called)
  148. self.assertFalse(s.unregister.called)
  149. def test_modify_unregister(self):
  150. # Make sure the fd is unregister()ed in case of error on
  151. # modify(): http://bugs.python.org/issue30014
  152. if self.SELECTOR.__name__ == 'EpollSelector':
  153. patch = unittest.mock.patch(
  154. 'selectors.EpollSelector._selector_cls')
  155. elif self.SELECTOR.__name__ == 'PollSelector':
  156. patch = unittest.mock.patch(
  157. 'selectors.PollSelector._selector_cls')
  158. elif self.SELECTOR.__name__ == 'DevpollSelector':
  159. patch = unittest.mock.patch(
  160. 'selectors.DevpollSelector._selector_cls')
  161. else:
  162. raise self.skipTest("")
  163. with patch as m:
  164. m.return_value.modify = unittest.mock.Mock(
  165. side_effect=ZeroDivisionError)
  166. s = self.SELECTOR()
  167. self.addCleanup(s.close)
  168. rd, wr = self.make_socketpair()
  169. s.register(rd, selectors.EVENT_READ)
  170. self.assertEqual(len(s._map), 1)
  171. with self.assertRaises(ZeroDivisionError):
  172. s.modify(rd, selectors.EVENT_WRITE)
  173. self.assertEqual(len(s._map), 0)
  174. def test_close(self):
  175. s = self.SELECTOR()
  176. self.addCleanup(s.close)
  177. mapping = s.get_map()
  178. rd, wr = self.make_socketpair()
  179. s.register(rd, selectors.EVENT_READ)
  180. s.register(wr, selectors.EVENT_WRITE)
  181. s.close()
  182. self.assertRaises(RuntimeError, s.get_key, rd)
  183. self.assertRaises(RuntimeError, s.get_key, wr)
  184. self.assertRaises(KeyError, mapping.__getitem__, rd)
  185. self.assertRaises(KeyError, mapping.__getitem__, wr)
  186. def test_get_key(self):
  187. s = self.SELECTOR()
  188. self.addCleanup(s.close)
  189. rd, wr = self.make_socketpair()
  190. key = s.register(rd, selectors.EVENT_READ, "data")
  191. self.assertEqual(key, s.get_key(rd))
  192. # unknown file obj
  193. self.assertRaises(KeyError, s.get_key, 999999)
  194. def test_get_map(self):
  195. s = self.SELECTOR()
  196. self.addCleanup(s.close)
  197. rd, wr = self.make_socketpair()
  198. keys = s.get_map()
  199. self.assertFalse(keys)
  200. self.assertEqual(len(keys), 0)
  201. self.assertEqual(list(keys), [])
  202. key = s.register(rd, selectors.EVENT_READ, "data")
  203. self.assertIn(rd, keys)
  204. self.assertEqual(key, keys[rd])
  205. self.assertEqual(len(keys), 1)
  206. self.assertEqual(list(keys), [rd.fileno()])
  207. self.assertEqual(list(keys.values()), [key])
  208. # unknown file obj
  209. with self.assertRaises(KeyError):
  210. keys[999999]
  211. # Read-only mapping
  212. with self.assertRaises(TypeError):
  213. del keys[rd]
  214. def test_select(self):
  215. s = self.SELECTOR()
  216. self.addCleanup(s.close)
  217. rd, wr = self.make_socketpair()
  218. s.register(rd, selectors.EVENT_READ)
  219. wr_key = s.register(wr, selectors.EVENT_WRITE)
  220. result = s.select()
  221. for key, events in result:
  222. self.assertTrue(isinstance(key, selectors.SelectorKey))
  223. self.assertTrue(events)
  224. self.assertFalse(events & ~(selectors.EVENT_READ |
  225. selectors.EVENT_WRITE))
  226. self.assertEqual([(wr_key, selectors.EVENT_WRITE)], result)
  227. def test_context_manager(self):
  228. s = self.SELECTOR()
  229. self.addCleanup(s.close)
  230. rd, wr = self.make_socketpair()
  231. with s as sel:
  232. sel.register(rd, selectors.EVENT_READ)
  233. sel.register(wr, selectors.EVENT_WRITE)
  234. self.assertRaises(RuntimeError, s.get_key, rd)
  235. self.assertRaises(RuntimeError, s.get_key, wr)
  236. def test_fileno(self):
  237. s = self.SELECTOR()
  238. self.addCleanup(s.close)
  239. if hasattr(s, 'fileno'):
  240. fd = s.fileno()
  241. self.assertTrue(isinstance(fd, int))
  242. self.assertGreaterEqual(fd, 0)
  243. def test_selector(self):
  244. s = self.SELECTOR()
  245. self.addCleanup(s.close)
  246. NUM_SOCKETS = 12
  247. MSG = b" This is a test."
  248. MSG_LEN = len(MSG)
  249. readers = []
  250. writers = []
  251. r2w = {}
  252. w2r = {}
  253. for i in range(NUM_SOCKETS):
  254. rd, wr = self.make_socketpair()
  255. s.register(rd, selectors.EVENT_READ)
  256. s.register(wr, selectors.EVENT_WRITE)
  257. readers.append(rd)
  258. writers.append(wr)
  259. r2w[rd] = wr
  260. w2r[wr] = rd
  261. bufs = []
  262. while writers:
  263. ready = s.select()
  264. ready_writers = find_ready_matching(ready, selectors.EVENT_WRITE)
  265. if not ready_writers:
  266. self.fail("no sockets ready for writing")
  267. wr = random.choice(ready_writers)
  268. wr.send(MSG)
  269. for i in range(10):
  270. ready = s.select()
  271. ready_readers = find_ready_matching(ready,
  272. selectors.EVENT_READ)
  273. if ready_readers:
  274. break
  275. # there might be a delay between the write to the write end and
  276. # the read end is reported ready
  277. sleep(0.1)
  278. else:
  279. self.fail("no sockets ready for reading")
  280. self.assertEqual([w2r[wr]], ready_readers)
  281. rd = ready_readers[0]
  282. buf = rd.recv(MSG_LEN)
  283. self.assertEqual(len(buf), MSG_LEN)
  284. bufs.append(buf)
  285. s.unregister(r2w[rd])
  286. s.unregister(rd)
  287. writers.remove(r2w[rd])
  288. self.assertEqual(bufs, [MSG] * NUM_SOCKETS)
  289. @unittest.skipIf(sys.platform == 'win32',
  290. 'select.select() cannot be used with empty fd sets')
  291. def test_empty_select(self):
  292. # Issue #23009: Make sure EpollSelector.select() works when no FD is
  293. # registered.
  294. s = self.SELECTOR()
  295. self.addCleanup(s.close)
  296. self.assertEqual(s.select(timeout=0), [])
  297. def test_timeout(self):
  298. s = self.SELECTOR()
  299. self.addCleanup(s.close)
  300. rd, wr = self.make_socketpair()
  301. s.register(wr, selectors.EVENT_WRITE)
  302. t = time()
  303. self.assertEqual(1, len(s.select(0)))
  304. self.assertEqual(1, len(s.select(-1)))
  305. self.assertLess(time() - t, 0.5)
  306. s.unregister(wr)
  307. s.register(rd, selectors.EVENT_READ)
  308. t = time()
  309. self.assertFalse(s.select(0))
  310. self.assertFalse(s.select(-1))
  311. self.assertLess(time() - t, 0.5)
  312. t0 = time()
  313. self.assertFalse(s.select(1))
  314. t1 = time()
  315. dt = t1 - t0
  316. # Tolerate 2.0 seconds for very slow buildbots
  317. self.assertTrue(0.8 <= dt <= 2.0, dt)
  318. @unittest.skipUnless(hasattr(signal, "alarm"),
  319. "signal.alarm() required for this test")
  320. def test_select_interrupt_exc(self):
  321. s = self.SELECTOR()
  322. self.addCleanup(s.close)
  323. rd, wr = self.make_socketpair()
  324. class InterruptSelect(Exception):
  325. pass
  326. def handler(*args):
  327. raise InterruptSelect
  328. orig_alrm_handler = signal.signal(signal.SIGALRM, handler)
  329. self.addCleanup(signal.signal, signal.SIGALRM, orig_alrm_handler)
  330. try:
  331. signal.alarm(1)
  332. s.register(rd, selectors.EVENT_READ)
  333. t = time()
  334. # select() is interrupted by a signal which raises an exception
  335. with self.assertRaises(InterruptSelect):
  336. s.select(30)
  337. # select() was interrupted before the timeout of 30 seconds
  338. self.assertLess(time() - t, 5.0)
  339. finally:
  340. signal.alarm(0)
  341. @unittest.skipUnless(hasattr(signal, "alarm"),
  342. "signal.alarm() required for this test")
  343. def test_select_interrupt_noraise(self):
  344. s = self.SELECTOR()
  345. self.addCleanup(s.close)
  346. rd, wr = self.make_socketpair()
  347. orig_alrm_handler = signal.signal(signal.SIGALRM, lambda *args: None)
  348. self.addCleanup(signal.signal, signal.SIGALRM, orig_alrm_handler)
  349. try:
  350. signal.alarm(1)
  351. s.register(rd, selectors.EVENT_READ)
  352. t = time()
  353. # select() is interrupted by a signal, but the signal handler doesn't
  354. # raise an exception, so select() should by retries with a recomputed
  355. # timeout
  356. self.assertFalse(s.select(1.5))
  357. self.assertGreaterEqual(time() - t, 1.0)
  358. finally:
  359. signal.alarm(0)
  360. class ScalableSelectorMixIn:
  361. # see issue #18963 for why it's skipped on older OS X versions
  362. @support.requires_mac_ver(10, 5)
  363. @unittest.skipUnless(resource, "Test needs resource module")
  364. def test_above_fd_setsize(self):
  365. # A scalable implementation should have no problem with more than
  366. # FD_SETSIZE file descriptors. Since we don't know the value, we just
  367. # try to set the soft RLIMIT_NOFILE to the hard RLIMIT_NOFILE ceiling.
  368. soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
  369. try:
  370. resource.setrlimit(resource.RLIMIT_NOFILE, (hard, hard))
  371. self.addCleanup(resource.setrlimit, resource.RLIMIT_NOFILE,
  372. (soft, hard))
  373. NUM_FDS = min(hard, 2**16)
  374. except (OSError, ValueError):
  375. NUM_FDS = soft
  376. # guard for already allocated FDs (stdin, stdout...)
  377. NUM_FDS -= 32
  378. s = self.SELECTOR()
  379. self.addCleanup(s.close)
  380. for i in range(NUM_FDS // 2):
  381. try:
  382. rd, wr = self.make_socketpair()
  383. except OSError:
  384. # too many FDs, skip - note that we should only catch EMFILE
  385. # here, but apparently *BSD and Solaris can fail upon connect()
  386. # or bind() with EADDRNOTAVAIL, so let's be safe
  387. self.skipTest("FD limit reached")
  388. try:
  389. s.register(rd, selectors.EVENT_READ)
  390. s.register(wr, selectors.EVENT_WRITE)
  391. except OSError as e:
  392. if e.errno == errno.ENOSPC:
  393. # this can be raised by epoll if we go over
  394. # fs.epoll.max_user_watches sysctl
  395. self.skipTest("FD limit reached")
  396. raise
  397. try:
  398. fds = s.select()
  399. except OSError as e:
  400. if e.errno == errno.EINVAL and sys.platform == 'darwin':
  401. # unexplainable errors on macOS don't need to fail the test
  402. self.skipTest("Invalid argument error calling poll()")
  403. raise
  404. self.assertEqual(NUM_FDS // 2, len(fds))
  405. class DefaultSelectorTestCase(BaseSelectorTestCase, unittest.TestCase):
  406. SELECTOR = selectors.DefaultSelector
  407. class SelectSelectorTestCase(BaseSelectorTestCase, unittest.TestCase):
  408. SELECTOR = selectors.SelectSelector
  409. @unittest.skipUnless(hasattr(selectors, 'PollSelector'),
  410. "Test needs selectors.PollSelector")
  411. class PollSelectorTestCase(BaseSelectorTestCase, ScalableSelectorMixIn,
  412. unittest.TestCase):
  413. SELECTOR = getattr(selectors, 'PollSelector', None)
  414. @unittest.skipUnless(hasattr(selectors, 'EpollSelector'),
  415. "Test needs selectors.EpollSelector")
  416. class EpollSelectorTestCase(BaseSelectorTestCase, ScalableSelectorMixIn,
  417. unittest.TestCase):
  418. SELECTOR = getattr(selectors, 'EpollSelector', None)
  419. def test_register_file(self):
  420. # epoll(7) returns EPERM when given a file to watch
  421. s = self.SELECTOR()
  422. with tempfile.NamedTemporaryFile() as f:
  423. with self.assertRaises(IOError):
  424. s.register(f, selectors.EVENT_READ)
  425. # the SelectorKey has been removed
  426. with self.assertRaises(KeyError):
  427. s.get_key(f)
  428. @unittest.skipUnless(hasattr(selectors, 'KqueueSelector'),
  429. "Test needs selectors.KqueueSelector)")
  430. class KqueueSelectorTestCase(BaseSelectorTestCase, ScalableSelectorMixIn,
  431. unittest.TestCase):
  432. SELECTOR = getattr(selectors, 'KqueueSelector', None)
  433. def test_register_bad_fd(self):
  434. # a file descriptor that's been closed should raise an OSError
  435. # with EBADF
  436. s = self.SELECTOR()
  437. bad_f = os_helper.make_bad_fd()
  438. with self.assertRaises(OSError) as cm:
  439. s.register(bad_f, selectors.EVENT_READ)
  440. self.assertEqual(cm.exception.errno, errno.EBADF)
  441. # the SelectorKey has been removed
  442. with self.assertRaises(KeyError):
  443. s.get_key(bad_f)
  444. def test_empty_select_timeout(self):
  445. # Issues #23009, #29255: Make sure timeout is applied when no fds
  446. # are registered.
  447. s = self.SELECTOR()
  448. self.addCleanup(s.close)
  449. t0 = time()
  450. self.assertEqual(s.select(1), [])
  451. t1 = time()
  452. dt = t1 - t0
  453. # Tolerate 2.0 seconds for very slow buildbots
  454. self.assertTrue(0.8 <= dt <= 2.0, dt)
  455. @unittest.skipUnless(hasattr(selectors, 'DevpollSelector'),
  456. "Test needs selectors.DevpollSelector")
  457. class DevpollSelectorTestCase(BaseSelectorTestCase, ScalableSelectorMixIn,
  458. unittest.TestCase):
  459. SELECTOR = getattr(selectors, 'DevpollSelector', None)
  460. def tearDownModule():
  461. support.reap_children()
  462. if __name__ == "__main__":
  463. unittest.main()