blob: 1bdf972cdf7387021a2b559362003cdf54157f7e [file] [log] [blame]
Charles-François Natali243d8d82013-09-04 19:02:49 +02001"""Selectors module.
2
3This module allows high-level and efficient I/O multiplexing, built upon the
4`select` module primitives.
5"""
6
7
8from abc import ABCMeta, abstractmethod
Charles-François Natali4574b492013-10-30 20:31:04 +01009from collections import namedtuple, Mapping
Charles-François Natali243d8d82013-09-04 19:02:49 +020010import functools
Victor Stinner11da8e22014-01-21 01:48:28 +010011import math
Charles-François Natali243d8d82013-09-04 19:02:49 +020012import select
13import sys
14
15
16# generic events, that must be mapped to implementation-specific ones
17EVENT_READ = (1 << 0)
18EVENT_WRITE = (1 << 1)
19
20
21def _fileobj_to_fd(fileobj):
22 """Return a file descriptor from a file object.
23
24 Parameters:
25 fileobj -- file object or file descriptor
26
27 Returns:
28 corresponding file descriptor
Guido van Rossum9710ff02013-12-07 15:57:01 -080029
30 Raises:
31 ValueError if the object is invalid
Charles-François Natali243d8d82013-09-04 19:02:49 +020032 """
33 if isinstance(fileobj, int):
34 fd = fileobj
35 else:
36 try:
37 fd = int(fileobj.fileno())
38 except (AttributeError, TypeError, ValueError):
39 raise ValueError("Invalid file object: "
40 "{!r}".format(fileobj)) from None
41 if fd < 0:
42 raise ValueError("Invalid file descriptor: {}".format(fd))
43 return fd
44
45
46SelectorKey = namedtuple('SelectorKey', ['fileobj', 'fd', 'events', 'data'])
47"""Object used to associate a file object to its backing file descriptor,
48selected event mask and attached data."""
49
50
Charles-François Natali4574b492013-10-30 20:31:04 +010051class _SelectorMapping(Mapping):
52 """Mapping of file objects to selector keys."""
53
54 def __init__(self, selector):
55 self._selector = selector
56
57 def __len__(self):
58 return len(self._selector._fd_to_key)
59
60 def __getitem__(self, fileobj):
61 try:
Guido van Rossum9710ff02013-12-07 15:57:01 -080062 fd = self._selector._fileobj_lookup(fileobj)
63 return self._selector._fd_to_key[fd]
Charles-François Natali4574b492013-10-30 20:31:04 +010064 except KeyError:
65 raise KeyError("{!r} is not registered".format(fileobj)) from None
66
67 def __iter__(self):
68 return iter(self._selector._fd_to_key)
69
70
Charles-François Natali243d8d82013-09-04 19:02:49 +020071class BaseSelector(metaclass=ABCMeta):
Charles-François Natalib3330a0a2013-12-01 11:04:17 +010072 """Selector abstract base class.
Charles-François Natali243d8d82013-09-04 19:02:49 +020073
74 A selector supports registering file objects to be monitored for specific
75 I/O events.
76
77 A file object is a file descriptor or any object with a `fileno()` method.
78 An arbitrary object can be attached to the file object, which can be used
79 for example to store context information, a callback, etc.
80
81 A selector can use various implementations (select(), poll(), epoll()...)
82 depending on the platform. The default `Selector` class uses the most
83 performant implementation on the current platform.
84 """
85
Charles-François Natalib3330a0a2013-12-01 11:04:17 +010086 @abstractmethod
Charles-François Natali243d8d82013-09-04 19:02:49 +020087 def register(self, fileobj, events, data=None):
88 """Register a file object.
89
90 Parameters:
91 fileobj -- file object or file descriptor
92 events -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE)
93 data -- attached data
94
95 Returns:
96 SelectorKey instance
Guido van Rossum9710ff02013-12-07 15:57:01 -080097
98 Raises:
99 ValueError if events is invalid
100 KeyError if fileobj is already registered
101 OSError if fileobj is closed or otherwise is unacceptable to
102 the underlying system call (if a system call is made)
103
104 Note:
105 OSError may or may not be raised
Charles-François Natali243d8d82013-09-04 19:02:49 +0200106 """
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100107 raise NotImplementedError
Charles-François Natali243d8d82013-09-04 19:02:49 +0200108
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100109 @abstractmethod
Charles-François Natali243d8d82013-09-04 19:02:49 +0200110 def unregister(self, fileobj):
111 """Unregister a file object.
112
113 Parameters:
114 fileobj -- file object or file descriptor
115
116 Returns:
117 SelectorKey instance
Guido van Rossum9710ff02013-12-07 15:57:01 -0800118
119 Raises:
120 KeyError if fileobj is not registered
121
122 Note:
123 If fileobj is registered but has since been closed this does
124 *not* raise OSError (even if the wrapped syscall does)
Charles-François Natali243d8d82013-09-04 19:02:49 +0200125 """
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100126 raise NotImplementedError
Charles-François Natali243d8d82013-09-04 19:02:49 +0200127
128 def modify(self, fileobj, events, data=None):
129 """Change a registered file object monitored events or attached data.
130
131 Parameters:
132 fileobj -- file object or file descriptor
133 events -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE)
134 data -- attached data
135
136 Returns:
137 SelectorKey instance
Guido van Rossum9710ff02013-12-07 15:57:01 -0800138
139 Raises:
140 Anything that unregister() or register() raises
Charles-François Natali243d8d82013-09-04 19:02:49 +0200141 """
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100142 self.unregister(fileobj)
143 return self.register(fileobj, events, data)
Charles-François Natali243d8d82013-09-04 19:02:49 +0200144
145 @abstractmethod
146 def select(self, timeout=None):
147 """Perform the actual selection, until some monitored file objects are
148 ready or a timeout expires.
149
150 Parameters:
151 timeout -- if timeout > 0, this specifies the maximum wait time, in
152 seconds
153 if timeout <= 0, the select() call won't block, and will
154 report the currently ready file objects
155 if timeout is None, select() will block until a monitored
156 file object becomes ready
157
158 Returns:
159 list of (key, events) for ready file objects
160 `events` is a bitwise mask of EVENT_READ|EVENT_WRITE
161 """
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100162 raise NotImplementedError
Charles-François Natali243d8d82013-09-04 19:02:49 +0200163
164 def close(self):
165 """Close the selector.
166
167 This must be called to make sure that any underlying resource is freed.
168 """
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100169 pass
Charles-François Natali243d8d82013-09-04 19:02:49 +0200170
171 def get_key(self, fileobj):
172 """Return the key associated to a registered file object.
173
174 Returns:
175 SelectorKey for this file object
176 """
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100177 mapping = self.get_map()
Charles-François Natali243d8d82013-09-04 19:02:49 +0200178 try:
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100179 return mapping[fileobj]
Charles-François Natali243d8d82013-09-04 19:02:49 +0200180 except KeyError:
181 raise KeyError("{!r} is not registered".format(fileobj)) from None
182
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100183 @abstractmethod
Charles-François Natali4574b492013-10-30 20:31:04 +0100184 def get_map(self):
185 """Return a mapping of file objects to selector keys."""
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100186 raise NotImplementedError
Charles-François Natali4574b492013-10-30 20:31:04 +0100187
Charles-François Natali243d8d82013-09-04 19:02:49 +0200188 def __enter__(self):
189 return self
190
191 def __exit__(self, *args):
192 self.close()
193
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100194
195class _BaseSelectorImpl(BaseSelector):
196 """Base selector implementation."""
197
198 def __init__(self):
199 # this maps file descriptors to keys
200 self._fd_to_key = {}
201 # read-only mapping returned by get_map()
202 self._map = _SelectorMapping(self)
203
Guido van Rossum9710ff02013-12-07 15:57:01 -0800204 def _fileobj_lookup(self, fileobj):
205 """Return a file descriptor from a file object.
206
207 This wraps _fileobj_to_fd() to do an exhaustive search in case
208 the object is invalid but we still have it in our map. This
209 is used by unregister() so we can unregister an object that
210 was previously registered even if it is closed. It is also
211 used by _SelectorMapping.
212 """
213 try:
214 return _fileobj_to_fd(fileobj)
215 except ValueError:
216 # Do an exhaustive search.
217 for key in self._fd_to_key.values():
218 if key.fileobj is fileobj:
219 return key.fd
220 # Raise ValueError after all.
221 raise
222
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100223 def register(self, fileobj, events, data=None):
224 if (not events) or (events & ~(EVENT_READ | EVENT_WRITE)):
225 raise ValueError("Invalid events: {!r}".format(events))
226
Guido van Rossum9710ff02013-12-07 15:57:01 -0800227 key = SelectorKey(fileobj, self._fileobj_lookup(fileobj), events, data)
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100228
229 if key.fd in self._fd_to_key:
Guido van Rossum9710ff02013-12-07 15:57:01 -0800230 raise KeyError("{!r} (FD {}) is already registered"
231 .format(fileobj, key.fd))
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100232
233 self._fd_to_key[key.fd] = key
234 return key
235
236 def unregister(self, fileobj):
237 try:
Guido van Rossum9710ff02013-12-07 15:57:01 -0800238 key = self._fd_to_key.pop(self._fileobj_lookup(fileobj))
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100239 except KeyError:
240 raise KeyError("{!r} is not registered".format(fileobj)) from None
241 return key
242
243 def modify(self, fileobj, events, data=None):
244 # TODO: Subclasses can probably optimize this even further.
245 try:
Guido van Rossum9710ff02013-12-07 15:57:01 -0800246 key = self._fd_to_key[self._fileobj_lookup(fileobj)]
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100247 except KeyError:
248 raise KeyError("{!r} is not registered".format(fileobj)) from None
249 if events != key.events:
250 self.unregister(fileobj)
251 key = self.register(fileobj, events, data)
252 elif data != key.data:
253 # Use a shortcut to update the data.
254 key = key._replace(data=data)
255 self._fd_to_key[key.fd] = key
256 return key
257
258 def close(self):
259 self._fd_to_key.clear()
260
261 def get_map(self):
262 return self._map
263
Charles-François Natali243d8d82013-09-04 19:02:49 +0200264 def _key_from_fd(self, fd):
265 """Return the key associated to a given file descriptor.
266
267 Parameters:
268 fd -- file descriptor
269
270 Returns:
271 corresponding key, or None if not found
272 """
273 try:
274 return self._fd_to_key[fd]
275 except KeyError:
276 return None
277
278
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100279class SelectSelector(_BaseSelectorImpl):
Charles-François Natali243d8d82013-09-04 19:02:49 +0200280 """Select-based selector."""
281
282 def __init__(self):
283 super().__init__()
284 self._readers = set()
285 self._writers = set()
286
287 def register(self, fileobj, events, data=None):
288 key = super().register(fileobj, events, data)
289 if events & EVENT_READ:
290 self._readers.add(key.fd)
291 if events & EVENT_WRITE:
292 self._writers.add(key.fd)
293 return key
294
295 def unregister(self, fileobj):
296 key = super().unregister(fileobj)
297 self._readers.discard(key.fd)
298 self._writers.discard(key.fd)
299 return key
300
301 if sys.platform == 'win32':
302 def _select(self, r, w, _, timeout=None):
303 r, w, x = select.select(r, w, w, timeout)
304 return r, w + x, []
305 else:
306 _select = select.select
307
308 def select(self, timeout=None):
309 timeout = None if timeout is None else max(timeout, 0)
310 ready = []
311 try:
312 r, w, _ = self._select(self._readers, self._writers, [], timeout)
313 except InterruptedError:
314 return ready
315 r = set(r)
316 w = set(w)
317 for fd in r | w:
318 events = 0
319 if fd in r:
320 events |= EVENT_READ
321 if fd in w:
322 events |= EVENT_WRITE
323
324 key = self._key_from_fd(fd)
325 if key:
326 ready.append((key, events & key.events))
327 return ready
328
329
330if hasattr(select, 'poll'):
331
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100332 class PollSelector(_BaseSelectorImpl):
Charles-François Natali243d8d82013-09-04 19:02:49 +0200333 """Poll-based selector."""
334
335 def __init__(self):
336 super().__init__()
337 self._poll = select.poll()
338
339 def register(self, fileobj, events, data=None):
340 key = super().register(fileobj, events, data)
341 poll_events = 0
342 if events & EVENT_READ:
343 poll_events |= select.POLLIN
344 if events & EVENT_WRITE:
345 poll_events |= select.POLLOUT
346 self._poll.register(key.fd, poll_events)
347 return key
348
349 def unregister(self, fileobj):
350 key = super().unregister(fileobj)
351 self._poll.unregister(key.fd)
352 return key
353
354 def select(self, timeout=None):
Victor Stinner11da8e22014-01-21 01:48:28 +0100355 if timeout is None:
356 timeout = None
Victor Stinner7067b5d2014-01-21 17:49:41 +0100357 elif timeout <= 0:
Victor Stinner11da8e22014-01-21 01:48:28 +0100358 timeout = 0
359 else:
Victor Stinner7067b5d2014-01-21 17:49:41 +0100360 # poll() has a resolution of 1 millisecond, round away from
361 # zero to wait *at least* timeout seconds.
362 timeout = int(math.ceil(timeout * 1e3))
Charles-François Natali243d8d82013-09-04 19:02:49 +0200363 ready = []
364 try:
365 fd_event_list = self._poll.poll(timeout)
366 except InterruptedError:
367 return ready
368 for fd, event in fd_event_list:
369 events = 0
370 if event & ~select.POLLIN:
371 events |= EVENT_WRITE
372 if event & ~select.POLLOUT:
373 events |= EVENT_READ
374
375 key = self._key_from_fd(fd)
376 if key:
377 ready.append((key, events & key.events))
378 return ready
379
380
381if hasattr(select, 'epoll'):
382
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100383 class EpollSelector(_BaseSelectorImpl):
Charles-François Natali243d8d82013-09-04 19:02:49 +0200384 """Epoll-based selector."""
385
386 def __init__(self):
387 super().__init__()
388 self._epoll = select.epoll()
389
390 def fileno(self):
391 return self._epoll.fileno()
392
393 def register(self, fileobj, events, data=None):
394 key = super().register(fileobj, events, data)
395 epoll_events = 0
396 if events & EVENT_READ:
397 epoll_events |= select.EPOLLIN
398 if events & EVENT_WRITE:
399 epoll_events |= select.EPOLLOUT
400 self._epoll.register(key.fd, epoll_events)
401 return key
402
403 def unregister(self, fileobj):
404 key = super().unregister(fileobj)
Guido van Rossum9710ff02013-12-07 15:57:01 -0800405 try:
406 self._epoll.unregister(key.fd)
407 except OSError:
408 # This can happen if the FD was closed since it
409 # was registered.
410 pass
Charles-François Natali243d8d82013-09-04 19:02:49 +0200411 return key
412
413 def select(self, timeout=None):
Victor Stinner567b26e2014-01-21 21:00:47 +0100414 if timeout is None:
415 timeout = -1
416 elif timeout <= 0:
417 timeout = 0
418 else:
419 # epoll_wait() has a resolution of 1 millisecond, round away
420 # from zero to wait *at least* timeout seconds.
421 timeout = math.ceil(timeout * 1e3) * 1e-3
Charles-François Natali243d8d82013-09-04 19:02:49 +0200422 max_ev = len(self._fd_to_key)
423 ready = []
424 try:
425 fd_event_list = self._epoll.poll(timeout, max_ev)
426 except InterruptedError:
427 return ready
428 for fd, event in fd_event_list:
429 events = 0
430 if event & ~select.EPOLLIN:
431 events |= EVENT_WRITE
432 if event & ~select.EPOLLOUT:
433 events |= EVENT_READ
434
435 key = self._key_from_fd(fd)
436 if key:
437 ready.append((key, events & key.events))
438 return ready
439
440 def close(self):
Charles-François Natali243d8d82013-09-04 19:02:49 +0200441 self._epoll.close()
Guido van Rossum61a2ced2013-10-31 11:01:40 -0700442 super().close()
Charles-François Natali243d8d82013-09-04 19:02:49 +0200443
444
445if hasattr(select, 'kqueue'):
446
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100447 class KqueueSelector(_BaseSelectorImpl):
Charles-François Natali243d8d82013-09-04 19:02:49 +0200448 """Kqueue-based selector."""
449
450 def __init__(self):
451 super().__init__()
452 self._kqueue = select.kqueue()
453
454 def fileno(self):
455 return self._kqueue.fileno()
456
457 def register(self, fileobj, events, data=None):
458 key = super().register(fileobj, events, data)
459 if events & EVENT_READ:
460 kev = select.kevent(key.fd, select.KQ_FILTER_READ,
461 select.KQ_EV_ADD)
462 self._kqueue.control([kev], 0, 0)
463 if events & EVENT_WRITE:
464 kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
465 select.KQ_EV_ADD)
466 self._kqueue.control([kev], 0, 0)
467 return key
468
469 def unregister(self, fileobj):
470 key = super().unregister(fileobj)
471 if key.events & EVENT_READ:
472 kev = select.kevent(key.fd, select.KQ_FILTER_READ,
473 select.KQ_EV_DELETE)
Guido van Rossum9710ff02013-12-07 15:57:01 -0800474 try:
475 self._kqueue.control([kev], 0, 0)
476 except OSError:
477 # This can happen if the FD was closed since it
478 # was registered.
479 pass
Charles-François Natali243d8d82013-09-04 19:02:49 +0200480 if key.events & EVENT_WRITE:
481 kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
482 select.KQ_EV_DELETE)
Guido van Rossum9710ff02013-12-07 15:57:01 -0800483 try:
484 self._kqueue.control([kev], 0, 0)
485 except OSError:
486 # See comment above.
487 pass
Charles-François Natali243d8d82013-09-04 19:02:49 +0200488 return key
489
490 def select(self, timeout=None):
491 timeout = None if timeout is None else max(timeout, 0)
492 max_ev = len(self._fd_to_key)
493 ready = []
494 try:
495 kev_list = self._kqueue.control(None, max_ev, timeout)
496 except InterruptedError:
497 return ready
498 for kev in kev_list:
499 fd = kev.ident
500 flag = kev.filter
501 events = 0
502 if flag == select.KQ_FILTER_READ:
503 events |= EVENT_READ
504 if flag == select.KQ_FILTER_WRITE:
505 events |= EVENT_WRITE
506
507 key = self._key_from_fd(fd)
508 if key:
509 ready.append((key, events & key.events))
510 return ready
511
512 def close(self):
Charles-François Natali243d8d82013-09-04 19:02:49 +0200513 self._kqueue.close()
Guido van Rossum61a2ced2013-10-31 11:01:40 -0700514 super().close()
Charles-François Natali243d8d82013-09-04 19:02:49 +0200515
516
517# Choose the best implementation: roughly, epoll|kqueue > poll > select.
518# select() also can't accept a FD > FD_SETSIZE (usually around 1024)
519if 'KqueueSelector' in globals():
520 DefaultSelector = KqueueSelector
521elif 'EpollSelector' in globals():
522 DefaultSelector = EpollSelector
523elif 'PollSelector' in globals():
524 DefaultSelector = PollSelector
525else:
526 DefaultSelector = SelectSelector