Mercurial > hg-stable
annotate mercurial/thirdparty/selectors2.py @ 39183:f39efa885a6d
revlog: also detect intermediate snapshots
Also detect intermediate-snapshot done against another previous snapshot.
Doing an intermediate snapshot instead of a full one can reduce the number of
full snapshots we need. They are especially useful for content with a lot of
churn on the same line (eg: the manifest) where having a delta over multiple
revisions can end up being significantly smaller than the sum of these
revision deltas.
A revlog built using intermediate snapshots can be a bit smaller and reuse
snapshot much more efficiently. This last property is useful combined with
constraints on chain length. Using intermediate snapshot can produce
repository with delta chain ten times shorter without impact on the storage
size. Shorter chain lengths are faster to restore, greatly improving read
performance.
This changesets (and the following ones) focus on getting the core principle
of intermediate snapshots into Mercurial core. Later changeset will introduce
the strategy to create them.
author | Paul Morelle <paul.morelle@octobus.net> |
---|---|
date | Fri, 20 Jul 2018 13:34:48 +0200 |
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 | |
35254
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() |