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