|
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() |