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