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