Mercurial > hg
annotate mercurial/selectors2.py @ 35137:5cd6682b5a90
obsolete: drop usage of changectx in '_computecontentdivergentset'
Changectx are expensive and not needed there. The use of `repo.set` denote old
code that predate the introduction of `repo.revs` that we now use.
On my mercurial repository 495 draft:
before: 0.054239 second
after: 0.046935 second
On a mercurial repository with 115973 draft:
before: 0.564548 second
after: 0.130534 second
author | Boris Feld <boris.feld@octobus.net> |
---|---|
date | Sun, 19 Nov 2017 05:34:50 +0100 |
parents | a568a46751b6 |
children |
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 # 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 | |
34639
a568a46751b6
selectors2: do not use platform.system()
Jun Wu <quark@fb.com>
parents:
33502
diff
changeset
|
37 from . import pycompat |
a568a46751b6
selectors2: do not use platform.system()
Jun Wu <quark@fb.com>
parents:
33502
diff
changeset
|
38 |
33502 | 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. | |
34639
a568a46751b6
selectors2: do not use platform.system()
Jun Wu <quark@fb.com>
parents:
33502
diff
changeset
|
292 if pycompat.isjython: |
33502 | 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: | |
34639
a568a46751b6
selectors2: do not use platform.system()
Jun Wu <quark@fb.com>
parents:
33502
diff
changeset
|
731 if pycompat.isjython: |
33502 | 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() |