Mercurial > public > mercurial-scm > hg
comparison mercurial/selectors2.py @ 33502:5d0c0c8d2929
selector2: vendor selector2 library
This library was a backport of the Python 3 "selectors" library. It is
useful to provide a better selector interface for Python2, to address some
issues of the plain old select.select, mentioned in the next patch.
The code [1] was ported using the MIT license, with some minor modifications
to make our test happy:
1. "# no-check-code" was added since it's foreign code.
2. "from __future__ import absolute_import" was added.
3. "from collections import namedtuple, Mapping" changed to avoid direct
symbol import.
[1]: https://github.com/SethMichaelLarson/selectors2/blob/d27dbd2fdc48331fb76ed431f44b6e6956de7f82/selectors2.py
# no-check-commit
author | Jun Wu <quark@fb.com> |
---|---|
date | Fri, 14 Jul 2017 20:19:46 -0700 |
parents | |
children | a568a46751b6 |
comparison
equal
deleted
inserted
replaced
33501:7008f6819002 | 33502:5d0c0c8d2929 |
---|---|
1 """ Back-ported, durable, and portable selectors """ | |
2 | |
3 # MIT License | |
4 # | |
5 # Copyright (c) 2017 Seth Michael Larson | |
6 # | |
7 # Permission is hereby granted, free of charge, to any person obtaining a copy | |
8 # of this software and associated documentation files (the "Software"), to deal | |
9 # in the Software without restriction, including without limitation the rights | |
10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
11 # copies of the Software, and to permit persons to whom the Software is | |
12 # furnished to do so, subject to the following conditions: | |
13 # | |
14 # The above copyright notice and this permission notice shall be included in all | |
15 # copies or substantial portions of the Software. | |
16 # | |
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
23 # SOFTWARE. | |
24 | |
25 # no-check-code | |
26 | |
27 from __future__ import absolute_import | |
28 | |
29 import collections | |
30 import errno | |
31 import math | |
32 import platform | |
33 import select | |
34 import socket | |
35 import sys | |
36 import time | |
37 | |
38 namedtuple = collections.namedtuple | |
39 Mapping = collections.Mapping | |
40 | |
41 try: | |
42 monotonic = time.monotonic | |
43 except AttributeError: | |
44 monotonic = time.time | |
45 | |
46 __author__ = 'Seth Michael Larson' | |
47 __email__ = 'sethmichaellarson@protonmail.com' | |
48 __version__ = '2.0.0' | |
49 __license__ = 'MIT' | |
50 __url__ = 'https://www.github.com/SethMichaelLarson/selectors2' | |
51 | |
52 __all__ = ['EVENT_READ', | |
53 'EVENT_WRITE', | |
54 'SelectorKey', | |
55 'DefaultSelector', | |
56 'BaseSelector'] | |
57 | |
58 EVENT_READ = (1 << 0) | |
59 EVENT_WRITE = (1 << 1) | |
60 _DEFAULT_SELECTOR = None | |
61 _SYSCALL_SENTINEL = object() # Sentinel in case a system call returns None. | |
62 _ERROR_TYPES = (OSError, IOError, socket.error) | |
63 | |
64 | |
65 SelectorKey = namedtuple('SelectorKey', ['fileobj', 'fd', 'events', 'data']) | |
66 | |
67 | |
68 class _SelectorMapping(Mapping): | |
69 """ Mapping of file objects to selector keys """ | |
70 | |
71 def __init__(self, selector): | |
72 self._selector = selector | |
73 | |
74 def __len__(self): | |
75 return len(self._selector._fd_to_key) | |
76 | |
77 def __getitem__(self, fileobj): | |
78 try: | |
79 fd = self._selector._fileobj_lookup(fileobj) | |
80 return self._selector._fd_to_key[fd] | |
81 except KeyError: | |
82 raise KeyError("{0!r} is not registered.".format(fileobj)) | |
83 | |
84 def __iter__(self): | |
85 return iter(self._selector._fd_to_key) | |
86 | |
87 | |
88 def _fileobj_to_fd(fileobj): | |
89 """ Return a file descriptor from a file object. If | |
90 given an integer will simply return that integer back. """ | |
91 if isinstance(fileobj, int): | |
92 fd = fileobj | |
93 else: | |
94 try: | |
95 fd = int(fileobj.fileno()) | |
96 except (AttributeError, TypeError, ValueError): | |
97 raise ValueError("Invalid file object: {0!r}".format(fileobj)) | |
98 if fd < 0: | |
99 raise ValueError("Invalid file descriptor: {0}".format(fd)) | |
100 return fd | |
101 | |
102 | |
103 class BaseSelector(object): | |
104 """ Abstract Selector class | |
105 | |
106 A selector supports registering file objects to be monitored | |
107 for specific I/O events. | |
108 | |
109 A file object is a file descriptor or any object with a | |
110 `fileno()` method. An arbitrary object can be attached to the | |
111 file object which can be used for example to store context info, | |
112 a callback, etc. | |
113 | |
114 A selector can use various implementations (select(), poll(), epoll(), | |
115 and kqueue()) depending on the platform. The 'DefaultSelector' class uses | |
116 the most efficient implementation for the current platform. | |
117 """ | |
118 def __init__(self): | |
119 # Maps file descriptors to keys. | |
120 self._fd_to_key = {} | |
121 | |
122 # Read-only mapping returned by get_map() | |
123 self._map = _SelectorMapping(self) | |
124 | |
125 def _fileobj_lookup(self, fileobj): | |
126 """ Return a file descriptor from a file object. | |
127 This wraps _fileobj_to_fd() to do an exhaustive | |
128 search in case the object is invalid but we still | |
129 have it in our map. Used by unregister() so we can | |
130 unregister an object that was previously registered | |
131 even if it is closed. It is also used by _SelectorMapping | |
132 """ | |
133 try: | |
134 return _fileobj_to_fd(fileobj) | |
135 except ValueError: | |
136 | |
137 # Search through all our mapped keys. | |
138 for key in self._fd_to_key.values(): | |
139 if key.fileobj is fileobj: | |
140 return key.fd | |
141 | |
142 # Raise ValueError after all. | |
143 raise | |
144 | |
145 def register(self, fileobj, events, data=None): | |
146 """ Register a file object for a set of events to monitor. """ | |
147 if (not events) or (events & ~(EVENT_READ | EVENT_WRITE)): | |
148 raise ValueError("Invalid events: {0!r}".format(events)) | |
149 | |
150 key = SelectorKey(fileobj, self._fileobj_lookup(fileobj), events, data) | |
151 | |
152 if key.fd in self._fd_to_key: | |
153 raise KeyError("{0!r} (FD {1}) is already registered" | |
154 .format(fileobj, key.fd)) | |
155 | |
156 self._fd_to_key[key.fd] = key | |
157 return key | |
158 | |
159 def unregister(self, fileobj): | |
160 """ Unregister a file object from being monitored. """ | |
161 try: | |
162 key = self._fd_to_key.pop(self._fileobj_lookup(fileobj)) | |
163 except KeyError: | |
164 raise KeyError("{0!r} is not registered".format(fileobj)) | |
165 | |
166 # Getting the fileno of a closed socket on Windows errors with EBADF. | |
167 except socket.error as err: | |
168 if err.errno != errno.EBADF: | |
169 raise | |
170 else: | |
171 for key in self._fd_to_key.values(): | |
172 if key.fileobj is fileobj: | |
173 self._fd_to_key.pop(key.fd) | |
174 break | |
175 else: | |
176 raise KeyError("{0!r} is not registered".format(fileobj)) | |
177 return key | |
178 | |
179 def modify(self, fileobj, events, data=None): | |
180 """ Change a registered file object monitored events and data. """ | |
181 # NOTE: Some subclasses optimize this operation even further. | |
182 try: | |
183 key = self._fd_to_key[self._fileobj_lookup(fileobj)] | |
184 except KeyError: | |
185 raise KeyError("{0!r} is not registered".format(fileobj)) | |
186 | |
187 if events != key.events: | |
188 self.unregister(fileobj) | |
189 key = self.register(fileobj, events, data) | |
190 | |
191 elif data != key.data: | |
192 # Use a shortcut to update the data. | |
193 key = key._replace(data=data) | |
194 self._fd_to_key[key.fd] = key | |
195 | |
196 return key | |
197 | |
198 def select(self, timeout=None): | |
199 """ Perform the actual selection until some monitored file objects | |
200 are ready or the timeout expires. """ | |
201 raise NotImplementedError() | |
202 | |
203 def close(self): | |
204 """ Close the selector. This must be called to ensure that all | |
205 underlying resources are freed. """ | |
206 self._fd_to_key.clear() | |
207 self._map = None | |
208 | |
209 def get_key(self, fileobj): | |
210 """ Return the key associated with a registered file object. """ | |
211 mapping = self.get_map() | |
212 if mapping is None: | |
213 raise RuntimeError("Selector is closed") | |
214 try: | |
215 return mapping[fileobj] | |
216 except KeyError: | |
217 raise KeyError("{0!r} is not registered".format(fileobj)) | |
218 | |
219 def get_map(self): | |
220 """ Return a mapping of file objects to selector keys """ | |
221 return self._map | |
222 | |
223 def _key_from_fd(self, fd): | |
224 """ Return the key associated to a given file descriptor | |
225 Return None if it is not found. """ | |
226 try: | |
227 return self._fd_to_key[fd] | |
228 except KeyError: | |
229 return None | |
230 | |
231 def __enter__(self): | |
232 return self | |
233 | |
234 def __exit__(self, *_): | |
235 self.close() | |
236 | |
237 | |
238 # Almost all platforms have select.select() | |
239 if hasattr(select, "select"): | |
240 class SelectSelector(BaseSelector): | |
241 """ Select-based selector. """ | |
242 def __init__(self): | |
243 super(SelectSelector, self).__init__() | |
244 self._readers = set() | |
245 self._writers = set() | |
246 | |
247 def register(self, fileobj, events, data=None): | |
248 key = super(SelectSelector, self).register(fileobj, events, data) | |
249 if events & EVENT_READ: | |
250 self._readers.add(key.fd) | |
251 if events & EVENT_WRITE: | |
252 self._writers.add(key.fd) | |
253 return key | |
254 | |
255 def unregister(self, fileobj): | |
256 key = super(SelectSelector, self).unregister(fileobj) | |
257 self._readers.discard(key.fd) | |
258 self._writers.discard(key.fd) | |
259 return key | |
260 | |
261 def select(self, timeout=None): | |
262 # Selecting on empty lists on Windows errors out. | |
263 if not len(self._readers) and not len(self._writers): | |
264 return [] | |
265 | |
266 timeout = None if timeout is None else max(timeout, 0.0) | |
267 ready = [] | |
268 r, w, _ = _syscall_wrapper(self._wrap_select, True, self._readers, | |
269 self._writers, timeout) | |
270 r = set(r) | |
271 w = set(w) | |
272 for fd in r | w: | |
273 events = 0 | |
274 if fd in r: | |
275 events |= EVENT_READ | |
276 if fd in w: | |
277 events |= EVENT_WRITE | |
278 | |
279 key = self._key_from_fd(fd) | |
280 if key: | |
281 ready.append((key, events & key.events)) | |
282 return ready | |
283 | |
284 def _wrap_select(self, r, w, timeout=None): | |
285 """ Wrapper for select.select because timeout is a positional arg """ | |
286 return select.select(r, w, [], timeout) | |
287 | |
288 __all__.append('SelectSelector') | |
289 | |
290 # Jython has a different implementation of .fileno() for socket objects. | |
291 if platform.system() == 'Java': | |
292 class _JythonSelectorMapping(object): | |
293 """ This is an implementation of _SelectorMapping that is built | |
294 for use specifically with Jython, which does not provide a hashable | |
295 value from socket.socket.fileno(). """ | |
296 | |
297 def __init__(self, selector): | |
298 assert isinstance(selector, JythonSelectSelector) | |
299 self._selector = selector | |
300 | |
301 def __len__(self): | |
302 return len(self._selector._sockets) | |
303 | |
304 def __getitem__(self, fileobj): | |
305 for sock, key in self._selector._sockets: | |
306 if sock is fileobj: | |
307 return key | |
308 else: | |
309 raise KeyError("{0!r} is not registered.".format(fileobj)) | |
310 | |
311 class JythonSelectSelector(SelectSelector): | |
312 """ This is an implementation of SelectSelector that is for Jython | |
313 which works around that Jython's socket.socket.fileno() does not | |
314 return an integer fd value. All SelectorKey.fd will be equal to -1 | |
315 and should not be used. This instead uses object id to compare fileobj | |
316 and will only use select.select as it's the only selector that allows | |
317 directly passing in socket objects rather than registering fds. | |
318 See: http://bugs.jython.org/issue1678 | |
319 https://wiki.python.org/jython/NewSocketModule#socket.fileno.28.29_does_not_return_an_integer | |
320 """ | |
321 | |
322 def __init__(self): | |
323 super(JythonSelectSelector, self).__init__() | |
324 | |
325 self._sockets = [] # Uses a list of tuples instead of dictionary. | |
326 self._map = _JythonSelectorMapping(self) | |
327 self._readers = [] | |
328 self._writers = [] | |
329 | |
330 # Jython has a select.cpython_compatible_select function in older versions. | |
331 self._select_func = getattr(select, 'cpython_compatible_select', select.select) | |
332 | |
333 def register(self, fileobj, events, data=None): | |
334 for sock, _ in self._sockets: | |
335 if sock is fileobj: | |
336 raise KeyError("{0!r} is already registered" | |
337 .format(fileobj, sock)) | |
338 | |
339 key = SelectorKey(fileobj, -1, events, data) | |
340 self._sockets.append((fileobj, key)) | |
341 | |
342 if events & EVENT_READ: | |
343 self._readers.append(fileobj) | |
344 if events & EVENT_WRITE: | |
345 self._writers.append(fileobj) | |
346 return key | |
347 | |
348 def unregister(self, fileobj): | |
349 for i, (sock, key) in enumerate(self._sockets): | |
350 if sock is fileobj: | |
351 break | |
352 else: | |
353 raise KeyError("{0!r} is not registered.".format(fileobj)) | |
354 | |
355 if key.events & EVENT_READ: | |
356 self._readers.remove(fileobj) | |
357 if key.events & EVENT_WRITE: | |
358 self._writers.remove(fileobj) | |
359 | |
360 del self._sockets[i] | |
361 return key | |
362 | |
363 def _wrap_select(self, r, w, timeout=None): | |
364 """ Wrapper for select.select because timeout is a positional arg """ | |
365 return self._select_func(r, w, [], timeout) | |
366 | |
367 __all__.append('JythonSelectSelector') | |
368 SelectSelector = JythonSelectSelector # Override so the wrong selector isn't used. | |
369 | |
370 | |
371 if hasattr(select, "poll"): | |
372 class PollSelector(BaseSelector): | |
373 """ Poll-based selector """ | |
374 def __init__(self): | |
375 super(PollSelector, self).__init__() | |
376 self._poll = select.poll() | |
377 | |
378 def register(self, fileobj, events, data=None): | |
379 key = super(PollSelector, self).register(fileobj, events, data) | |
380 event_mask = 0 | |
381 if events & EVENT_READ: | |
382 event_mask |= select.POLLIN | |
383 if events & EVENT_WRITE: | |
384 event_mask |= select.POLLOUT | |
385 self._poll.register(key.fd, event_mask) | |
386 return key | |
387 | |
388 def unregister(self, fileobj): | |
389 key = super(PollSelector, self).unregister(fileobj) | |
390 self._poll.unregister(key.fd) | |
391 return key | |
392 | |
393 def _wrap_poll(self, timeout=None): | |
394 """ Wrapper function for select.poll.poll() so that | |
395 _syscall_wrapper can work with only seconds. """ | |
396 if timeout is not None: | |
397 if timeout <= 0: | |
398 timeout = 0 | |
399 else: | |
400 # select.poll.poll() has a resolution of 1 millisecond, | |
401 # round away from zero to wait *at least* timeout seconds. | |
402 timeout = math.ceil(timeout * 1000) | |
403 | |
404 result = self._poll.poll(timeout) | |
405 return result | |
406 | |
407 def select(self, timeout=None): | |
408 ready = [] | |
409 fd_events = _syscall_wrapper(self._wrap_poll, True, timeout=timeout) | |
410 for fd, event_mask in fd_events: | |
411 events = 0 | |
412 if event_mask & ~select.POLLIN: | |
413 events |= EVENT_WRITE | |
414 if event_mask & ~select.POLLOUT: | |
415 events |= EVENT_READ | |
416 | |
417 key = self._key_from_fd(fd) | |
418 if key: | |
419 ready.append((key, events & key.events)) | |
420 | |
421 return ready | |
422 | |
423 __all__.append('PollSelector') | |
424 | |
425 if hasattr(select, "epoll"): | |
426 class EpollSelector(BaseSelector): | |
427 """ Epoll-based selector """ | |
428 def __init__(self): | |
429 super(EpollSelector, self).__init__() | |
430 self._epoll = select.epoll() | |
431 | |
432 def fileno(self): | |
433 return self._epoll.fileno() | |
434 | |
435 def register(self, fileobj, events, data=None): | |
436 key = super(EpollSelector, self).register(fileobj, events, data) | |
437 events_mask = 0 | |
438 if events & EVENT_READ: | |
439 events_mask |= select.EPOLLIN | |
440 if events & EVENT_WRITE: | |
441 events_mask |= select.EPOLLOUT | |
442 _syscall_wrapper(self._epoll.register, False, key.fd, events_mask) | |
443 return key | |
444 | |
445 def unregister(self, fileobj): | |
446 key = super(EpollSelector, self).unregister(fileobj) | |
447 try: | |
448 _syscall_wrapper(self._epoll.unregister, False, key.fd) | |
449 except _ERROR_TYPES: | |
450 # This can occur when the fd was closed since registry. | |
451 pass | |
452 return key | |
453 | |
454 def select(self, timeout=None): | |
455 if timeout is not None: | |
456 if timeout <= 0: | |
457 timeout = 0.0 | |
458 else: | |
459 # select.epoll.poll() has a resolution of 1 millisecond | |
460 # but luckily takes seconds so we don't need a wrapper | |
461 # like PollSelector. Just for better rounding. | |
462 timeout = math.ceil(timeout * 1000) * 0.001 | |
463 timeout = float(timeout) | |
464 else: | |
465 timeout = -1.0 # epoll.poll() must have a float. | |
466 | |
467 # We always want at least 1 to ensure that select can be called | |
468 # with no file descriptors registered. Otherwise will fail. | |
469 max_events = max(len(self._fd_to_key), 1) | |
470 | |
471 ready = [] | |
472 fd_events = _syscall_wrapper(self._epoll.poll, True, | |
473 timeout=timeout, | |
474 maxevents=max_events) | |
475 for fd, event_mask in fd_events: | |
476 events = 0 | |
477 if event_mask & ~select.EPOLLIN: | |
478 events |= EVENT_WRITE | |
479 if event_mask & ~select.EPOLLOUT: | |
480 events |= EVENT_READ | |
481 | |
482 key = self._key_from_fd(fd) | |
483 if key: | |
484 ready.append((key, events & key.events)) | |
485 return ready | |
486 | |
487 def close(self): | |
488 self._epoll.close() | |
489 super(EpollSelector, self).close() | |
490 | |
491 __all__.append('EpollSelector') | |
492 | |
493 | |
494 if hasattr(select, "devpoll"): | |
495 class DevpollSelector(BaseSelector): | |
496 """Solaris /dev/poll selector.""" | |
497 | |
498 def __init__(self): | |
499 super(DevpollSelector, self).__init__() | |
500 self._devpoll = select.devpoll() | |
501 | |
502 def fileno(self): | |
503 return self._devpoll.fileno() | |
504 | |
505 def register(self, fileobj, events, data=None): | |
506 key = super(DevpollSelector, self).register(fileobj, events, data) | |
507 poll_events = 0 | |
508 if events & EVENT_READ: | |
509 poll_events |= select.POLLIN | |
510 if events & EVENT_WRITE: | |
511 poll_events |= select.POLLOUT | |
512 self._devpoll.register(key.fd, poll_events) | |
513 return key | |
514 | |
515 def unregister(self, fileobj): | |
516 key = super(DevpollSelector, self).unregister(fileobj) | |
517 self._devpoll.unregister(key.fd) | |
518 return key | |
519 | |
520 def _wrap_poll(self, timeout=None): | |
521 """ Wrapper function for select.poll.poll() so that | |
522 _syscall_wrapper can work with only seconds. """ | |
523 if timeout is not None: | |
524 if timeout <= 0: | |
525 timeout = 0 | |
526 else: | |
527 # select.devpoll.poll() has a resolution of 1 millisecond, | |
528 # round away from zero to wait *at least* timeout seconds. | |
529 timeout = math.ceil(timeout * 1000) | |
530 | |
531 result = self._devpoll.poll(timeout) | |
532 return result | |
533 | |
534 def select(self, timeout=None): | |
535 ready = [] | |
536 fd_events = _syscall_wrapper(self._wrap_poll, True, timeout=timeout) | |
537 for fd, event_mask in fd_events: | |
538 events = 0 | |
539 if event_mask & ~select.POLLIN: | |
540 events |= EVENT_WRITE | |
541 if event_mask & ~select.POLLOUT: | |
542 events |= EVENT_READ | |
543 | |
544 key = self._key_from_fd(fd) | |
545 if key: | |
546 ready.append((key, events & key.events)) | |
547 | |
548 return ready | |
549 | |
550 def close(self): | |
551 self._devpoll.close() | |
552 super(DevpollSelector, self).close() | |
553 | |
554 __all__.append('DevpollSelector') | |
555 | |
556 | |
557 if hasattr(select, "kqueue"): | |
558 class KqueueSelector(BaseSelector): | |
559 """ Kqueue / Kevent-based selector """ | |
560 def __init__(self): | |
561 super(KqueueSelector, self).__init__() | |
562 self._kqueue = select.kqueue() | |
563 | |
564 def fileno(self): | |
565 return self._kqueue.fileno() | |
566 | |
567 def register(self, fileobj, events, data=None): | |
568 key = super(KqueueSelector, self).register(fileobj, events, data) | |
569 if events & EVENT_READ: | |
570 kevent = select.kevent(key.fd, | |
571 select.KQ_FILTER_READ, | |
572 select.KQ_EV_ADD) | |
573 | |
574 _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0) | |
575 | |
576 if events & EVENT_WRITE: | |
577 kevent = select.kevent(key.fd, | |
578 select.KQ_FILTER_WRITE, | |
579 select.KQ_EV_ADD) | |
580 | |
581 _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0) | |
582 | |
583 return key | |
584 | |
585 def unregister(self, fileobj): | |
586 key = super(KqueueSelector, self).unregister(fileobj) | |
587 if key.events & EVENT_READ: | |
588 kevent = select.kevent(key.fd, | |
589 select.KQ_FILTER_READ, | |
590 select.KQ_EV_DELETE) | |
591 try: | |
592 _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0) | |
593 except _ERROR_TYPES: | |
594 pass | |
595 if key.events & EVENT_WRITE: | |
596 kevent = select.kevent(key.fd, | |
597 select.KQ_FILTER_WRITE, | |
598 select.KQ_EV_DELETE) | |
599 try: | |
600 _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0) | |
601 except _ERROR_TYPES: | |
602 pass | |
603 | |
604 return key | |
605 | |
606 def select(self, timeout=None): | |
607 if timeout is not None: | |
608 timeout = max(timeout, 0) | |
609 | |
610 max_events = len(self._fd_to_key) * 2 | |
611 ready_fds = {} | |
612 | |
613 kevent_list = _syscall_wrapper(self._kqueue.control, True, | |
614 None, max_events, timeout) | |
615 | |
616 for kevent in kevent_list: | |
617 fd = kevent.ident | |
618 event_mask = kevent.filter | |
619 events = 0 | |
620 if event_mask == select.KQ_FILTER_READ: | |
621 events |= EVENT_READ | |
622 if event_mask == select.KQ_FILTER_WRITE: | |
623 events |= EVENT_WRITE | |
624 | |
625 key = self._key_from_fd(fd) | |
626 if key: | |
627 if key.fd not in ready_fds: | |
628 ready_fds[key.fd] = (key, events & key.events) | |
629 else: | |
630 old_events = ready_fds[key.fd][1] | |
631 ready_fds[key.fd] = (key, (events | old_events) & key.events) | |
632 | |
633 return list(ready_fds.values()) | |
634 | |
635 def close(self): | |
636 self._kqueue.close() | |
637 super(KqueueSelector, self).close() | |
638 | |
639 __all__.append('KqueueSelector') | |
640 | |
641 | |
642 def _can_allocate(struct): | |
643 """ Checks that select structs can be allocated by the underlying | |
644 operating system, not just advertised by the select module. We don't | |
645 check select() because we'll be hopeful that most platforms that | |
646 don't have it available will not advertise it. (ie: GAE) """ | |
647 try: | |
648 # select.poll() objects won't fail until used. | |
649 if struct == 'poll': | |
650 p = select.poll() | |
651 p.poll(0) | |
652 | |
653 # All others will fail on allocation. | |
654 else: | |
655 getattr(select, struct)().close() | |
656 return True | |
657 except (OSError, AttributeError): | |
658 return False | |
659 | |
660 | |
661 # Python 3.5 uses a more direct route to wrap system calls to increase speed. | |
662 if sys.version_info >= (3, 5): | |
663 def _syscall_wrapper(func, _, *args, **kwargs): | |
664 """ This is the short-circuit version of the below logic | |
665 because in Python 3.5+ all selectors restart system calls. """ | |
666 return func(*args, **kwargs) | |
667 else: | |
668 def _syscall_wrapper(func, recalc_timeout, *args, **kwargs): | |
669 """ Wrapper function for syscalls that could fail due to EINTR. | |
670 All functions should be retried if there is time left in the timeout | |
671 in accordance with PEP 475. """ | |
672 timeout = kwargs.get("timeout", None) | |
673 if timeout is None: | |
674 expires = None | |
675 recalc_timeout = False | |
676 else: | |
677 timeout = float(timeout) | |
678 if timeout < 0.0: # Timeout less than 0 treated as no timeout. | |
679 expires = None | |
680 else: | |
681 expires = monotonic() + timeout | |
682 | |
683 args = list(args) | |
684 if recalc_timeout and "timeout" not in kwargs: | |
685 raise ValueError( | |
686 "Timeout must be in args or kwargs to be recalculated") | |
687 | |
688 result = _SYSCALL_SENTINEL | |
689 while result is _SYSCALL_SENTINEL: | |
690 try: | |
691 result = func(*args, **kwargs) | |
692 # OSError is thrown by select.select | |
693 # IOError is thrown by select.epoll.poll | |
694 # select.error is thrown by select.poll.poll | |
695 # Aren't we thankful for Python 3.x rework for exceptions? | |
696 except (OSError, IOError, select.error) as e: | |
697 # select.error wasn't a subclass of OSError in the past. | |
698 errcode = None | |
699 if hasattr(e, "errno"): | |
700 errcode = e.errno | |
701 elif hasattr(e, "args"): | |
702 errcode = e.args[0] | |
703 | |
704 # Also test for the Windows equivalent of EINTR. | |
705 is_interrupt = (errcode == errno.EINTR or (hasattr(errno, "WSAEINTR") and | |
706 errcode == errno.WSAEINTR)) | |
707 | |
708 if is_interrupt: | |
709 if expires is not None: | |
710 current_time = monotonic() | |
711 if current_time > expires: | |
712 raise OSError(errno=errno.ETIMEDOUT) | |
713 if recalc_timeout: | |
714 if "timeout" in kwargs: | |
715 kwargs["timeout"] = expires - current_time | |
716 continue | |
717 raise | |
718 return result | |
719 | |
720 | |
721 # Choose the best implementation, roughly: | |
722 # kqueue == devpoll == epoll > poll > select | |
723 # select() also can't accept a FD > FD_SETSIZE (usually around 1024) | |
724 def DefaultSelector(): | |
725 """ This function serves as a first call for DefaultSelector to | |
726 detect if the select module is being monkey-patched incorrectly | |
727 by eventlet, greenlet, and preserve proper behavior. """ | |
728 global _DEFAULT_SELECTOR | |
729 if _DEFAULT_SELECTOR is None: | |
730 if platform.system() == 'Java': # Platform-specific: Jython | |
731 _DEFAULT_SELECTOR = JythonSelectSelector | |
732 elif _can_allocate('kqueue'): | |
733 _DEFAULT_SELECTOR = KqueueSelector | |
734 elif _can_allocate('devpoll'): | |
735 _DEFAULT_SELECTOR = DevpollSelector | |
736 elif _can_allocate('epoll'): | |
737 _DEFAULT_SELECTOR = EpollSelector | |
738 elif _can_allocate('poll'): | |
739 _DEFAULT_SELECTOR = PollSelector | |
740 elif hasattr(select, 'select'): | |
741 _DEFAULT_SELECTOR = SelectSelector | |
742 else: # Platform-specific: AppEngine | |
743 raise RuntimeError('Platform does not have a selector.') | |
744 return _DEFAULT_SELECTOR() |