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