blob: 52ee8dbd564540b0c85d405d512da73586ca2cd1 [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
Victor Stinner635fca92014-01-25 14:56:48 +01008from abc import ABCMeta, abstractmethod, abstractproperty
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 Stinnerdcd97402014-01-31 12:12:53 +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
Victor Stinner635fca92014-01-25 14:56:48 +010086 @abstractproperty
87 def resolution(self):
88 """Resolution of the selector in seconds"""
89 return None
90
Charles-François Natalib3330a0a2013-12-01 11:04:17 +010091 @abstractmethod
Charles-François Natali243d8d82013-09-04 19:02:49 +020092 def register(self, fileobj, events, data=None):
93 """Register a file object.
94
95 Parameters:
96 fileobj -- file object or file descriptor
97 events -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE)
98 data -- attached data
99
100 Returns:
101 SelectorKey instance
Guido van Rossum9710ff02013-12-07 15:57:01 -0800102
103 Raises:
104 ValueError if events is invalid
105 KeyError if fileobj is already registered
106 OSError if fileobj is closed or otherwise is unacceptable to
107 the underlying system call (if a system call is made)
108
109 Note:
110 OSError may or may not be raised
Charles-François Natali243d8d82013-09-04 19:02:49 +0200111 """
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100112 raise NotImplementedError
Charles-François Natali243d8d82013-09-04 19:02:49 +0200113
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100114 @abstractmethod
Charles-François Natali243d8d82013-09-04 19:02:49 +0200115 def unregister(self, fileobj):
116 """Unregister a file object.
117
118 Parameters:
119 fileobj -- file object or file descriptor
120
121 Returns:
122 SelectorKey instance
Guido van Rossum9710ff02013-12-07 15:57:01 -0800123
124 Raises:
125 KeyError if fileobj is not registered
126
127 Note:
128 If fileobj is registered but has since been closed this does
129 *not* raise OSError (even if the wrapped syscall does)
Charles-François Natali243d8d82013-09-04 19:02:49 +0200130 """
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100131 raise NotImplementedError
Charles-François Natali243d8d82013-09-04 19:02:49 +0200132
133 def modify(self, fileobj, events, data=None):
134 """Change a registered file object monitored events or attached data.
135
136 Parameters:
137 fileobj -- file object or file descriptor
138 events -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE)
139 data -- attached data
140
141 Returns:
142 SelectorKey instance
Guido van Rossum9710ff02013-12-07 15:57:01 -0800143
144 Raises:
145 Anything that unregister() or register() raises
Charles-François Natali243d8d82013-09-04 19:02:49 +0200146 """
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100147 self.unregister(fileobj)
148 return self.register(fileobj, events, data)
Charles-François Natali243d8d82013-09-04 19:02:49 +0200149
150 @abstractmethod
151 def select(self, timeout=None):
152 """Perform the actual selection, until some monitored file objects are
153 ready or a timeout expires.
154
155 Parameters:
156 timeout -- if timeout > 0, this specifies the maximum wait time, in
157 seconds
158 if timeout <= 0, the select() call won't block, and will
159 report the currently ready file objects
160 if timeout is None, select() will block until a monitored
161 file object becomes ready
162
163 Returns:
164 list of (key, events) for ready file objects
165 `events` is a bitwise mask of EVENT_READ|EVENT_WRITE
166 """
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100167 raise NotImplementedError
Charles-François Natali243d8d82013-09-04 19:02:49 +0200168
169 def close(self):
170 """Close the selector.
171
172 This must be called to make sure that any underlying resource is freed.
173 """
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100174 pass
Charles-François Natali243d8d82013-09-04 19:02:49 +0200175
176 def get_key(self, fileobj):
177 """Return the key associated to a registered file object.
178
179 Returns:
180 SelectorKey for this file object
181 """
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100182 mapping = self.get_map()
Charles-François Natali243d8d82013-09-04 19:02:49 +0200183 try:
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100184 return mapping[fileobj]
Charles-François Natali243d8d82013-09-04 19:02:49 +0200185 except KeyError:
186 raise KeyError("{!r} is not registered".format(fileobj)) from None
187
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100188 @abstractmethod
Charles-François Natali4574b492013-10-30 20:31:04 +0100189 def get_map(self):
190 """Return a mapping of file objects to selector keys."""
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100191 raise NotImplementedError
Charles-François Natali4574b492013-10-30 20:31:04 +0100192
Charles-François Natali243d8d82013-09-04 19:02:49 +0200193 def __enter__(self):
194 return self
195
196 def __exit__(self, *args):
197 self.close()
198
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100199
200class _BaseSelectorImpl(BaseSelector):
201 """Base selector implementation."""
202
203 def __init__(self):
204 # this maps file descriptors to keys
205 self._fd_to_key = {}
206 # read-only mapping returned by get_map()
207 self._map = _SelectorMapping(self)
208
Guido van Rossum9710ff02013-12-07 15:57:01 -0800209 def _fileobj_lookup(self, fileobj):
210 """Return a file descriptor from a file object.
211
212 This wraps _fileobj_to_fd() to do an exhaustive search in case
213 the object is invalid but we still have it in our map. This
214 is used by unregister() so we can unregister an object that
215 was previously registered even if it is closed. It is also
216 used by _SelectorMapping.
217 """
218 try:
219 return _fileobj_to_fd(fileobj)
220 except ValueError:
221 # Do an exhaustive search.
222 for key in self._fd_to_key.values():
223 if key.fileobj is fileobj:
224 return key.fd
225 # Raise ValueError after all.
226 raise
227
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100228 def register(self, fileobj, events, data=None):
229 if (not events) or (events & ~(EVENT_READ | EVENT_WRITE)):
230 raise ValueError("Invalid events: {!r}".format(events))
231
Guido van Rossum9710ff02013-12-07 15:57:01 -0800232 key = SelectorKey(fileobj, self._fileobj_lookup(fileobj), events, data)
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100233
234 if key.fd in self._fd_to_key:
Guido van Rossum9710ff02013-12-07 15:57:01 -0800235 raise KeyError("{!r} (FD {}) is already registered"
236 .format(fileobj, key.fd))
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100237
238 self._fd_to_key[key.fd] = key
239 return key
240
241 def unregister(self, fileobj):
242 try:
Guido van Rossum9710ff02013-12-07 15:57:01 -0800243 key = self._fd_to_key.pop(self._fileobj_lookup(fileobj))
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100244 except KeyError:
245 raise KeyError("{!r} is not registered".format(fileobj)) from None
246 return key
247
248 def modify(self, fileobj, events, data=None):
249 # TODO: Subclasses can probably optimize this even further.
250 try:
Guido van Rossum9710ff02013-12-07 15:57:01 -0800251 key = self._fd_to_key[self._fileobj_lookup(fileobj)]
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100252 except KeyError:
253 raise KeyError("{!r} is not registered".format(fileobj)) from None
254 if events != key.events:
255 self.unregister(fileobj)
256 key = self.register(fileobj, events, data)
257 elif data != key.data:
258 # Use a shortcut to update the data.
259 key = key._replace(data=data)
260 self._fd_to_key[key.fd] = key
261 return key
262
263 def close(self):
264 self._fd_to_key.clear()
265
266 def get_map(self):
267 return self._map
268
Charles-François Natali243d8d82013-09-04 19:02:49 +0200269 def _key_from_fd(self, fd):
270 """Return the key associated to a given file descriptor.
271
272 Parameters:
273 fd -- file descriptor
274
275 Returns:
276 corresponding key, or None if not found
277 """
278 try:
279 return self._fd_to_key[fd]
280 except KeyError:
281 return None
282
283
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100284class SelectSelector(_BaseSelectorImpl):
Charles-François Natali243d8d82013-09-04 19:02:49 +0200285 """Select-based selector."""
286
287 def __init__(self):
288 super().__init__()
289 self._readers = set()
290 self._writers = set()
291
Victor Stinner635fca92014-01-25 14:56:48 +0100292 @property
293 def resolution(self):
294 return 1e-6
295
Charles-François Natali243d8d82013-09-04 19:02:49 +0200296 def register(self, fileobj, events, data=None):
297 key = super().register(fileobj, events, data)
298 if events & EVENT_READ:
299 self._readers.add(key.fd)
300 if events & EVENT_WRITE:
301 self._writers.add(key.fd)
302 return key
303
304 def unregister(self, fileobj):
305 key = super().unregister(fileobj)
306 self._readers.discard(key.fd)
307 self._writers.discard(key.fd)
308 return key
309
310 if sys.platform == 'win32':
311 def _select(self, r, w, _, timeout=None):
312 r, w, x = select.select(r, w, w, timeout)
313 return r, w + x, []
314 else:
315 _select = select.select
316
317 def select(self, timeout=None):
318 timeout = None if timeout is None else max(timeout, 0)
319 ready = []
320 try:
321 r, w, _ = self._select(self._readers, self._writers, [], timeout)
322 except InterruptedError:
323 return ready
324 r = set(r)
325 w = set(w)
326 for fd in r | w:
327 events = 0
328 if fd in r:
329 events |= EVENT_READ
330 if fd in w:
331 events |= EVENT_WRITE
332
333 key = self._key_from_fd(fd)
334 if key:
335 ready.append((key, events & key.events))
336 return ready
337
338
339if hasattr(select, 'poll'):
340
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100341 class PollSelector(_BaseSelectorImpl):
Charles-François Natali243d8d82013-09-04 19:02:49 +0200342 """Poll-based selector."""
343
344 def __init__(self):
345 super().__init__()
346 self._poll = select.poll()
347
Victor Stinner635fca92014-01-25 14:56:48 +0100348 @property
349 def resolution(self):
350 return 1e-3
351
Charles-François Natali243d8d82013-09-04 19:02:49 +0200352 def register(self, fileobj, events, data=None):
353 key = super().register(fileobj, events, data)
354 poll_events = 0
355 if events & EVENT_READ:
356 poll_events |= select.POLLIN
357 if events & EVENT_WRITE:
358 poll_events |= select.POLLOUT
359 self._poll.register(key.fd, poll_events)
360 return key
361
362 def unregister(self, fileobj):
363 key = super().unregister(fileobj)
364 self._poll.unregister(key.fd)
365 return key
366
367 def select(self, timeout=None):
Victor Stinner11da8e22014-01-21 01:48:28 +0100368 if timeout is None:
369 timeout = None
Victor Stinner7067b5d2014-01-21 17:49:41 +0100370 elif timeout <= 0:
Victor Stinner11da8e22014-01-21 01:48:28 +0100371 timeout = 0
372 else:
Victor Stinnerdcd97402014-01-31 12:12:53 +0100373 # poll() has a resolution of 1 millisecond, round away from
374 # zero to wait *at least* timeout seconds.
375 timeout = int(math.ceil(timeout * 1e3))
Charles-François Natali243d8d82013-09-04 19:02:49 +0200376 ready = []
377 try:
378 fd_event_list = self._poll.poll(timeout)
379 except InterruptedError:
380 return ready
381 for fd, event in fd_event_list:
382 events = 0
383 if event & ~select.POLLIN:
384 events |= EVENT_WRITE
385 if event & ~select.POLLOUT:
386 events |= EVENT_READ
387
388 key = self._key_from_fd(fd)
389 if key:
390 ready.append((key, events & key.events))
391 return ready
392
393
394if hasattr(select, 'epoll'):
395
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100396 class EpollSelector(_BaseSelectorImpl):
Charles-François Natali243d8d82013-09-04 19:02:49 +0200397 """Epoll-based selector."""
398
399 def __init__(self):
400 super().__init__()
401 self._epoll = select.epoll()
402
Victor Stinner635fca92014-01-25 14:56:48 +0100403 @property
404 def resolution(self):
405 return 1e-3
406
Charles-François Natali243d8d82013-09-04 19:02:49 +0200407 def fileno(self):
408 return self._epoll.fileno()
409
410 def register(self, fileobj, events, data=None):
411 key = super().register(fileobj, events, data)
412 epoll_events = 0
413 if events & EVENT_READ:
414 epoll_events |= select.EPOLLIN
415 if events & EVENT_WRITE:
416 epoll_events |= select.EPOLLOUT
417 self._epoll.register(key.fd, epoll_events)
418 return key
419
420 def unregister(self, fileobj):
421 key = super().unregister(fileobj)
Guido van Rossum9710ff02013-12-07 15:57:01 -0800422 try:
423 self._epoll.unregister(key.fd)
424 except OSError:
425 # This can happen if the FD was closed since it
426 # was registered.
427 pass
Charles-François Natali243d8d82013-09-04 19:02:49 +0200428 return key
429
430 def select(self, timeout=None):
Victor Stinner567b26e2014-01-21 21:00:47 +0100431 if timeout is None:
432 timeout = -1
433 elif timeout <= 0:
434 timeout = 0
Victor Stinnerdcd97402014-01-31 12:12:53 +0100435 else:
436 # epoll_wait() has a resolution of 1 millisecond, round away
437 # from zero to wait *at least* timeout seconds.
438 timeout = math.ceil(timeout * 1e3) * 1e-3
Charles-François Natali243d8d82013-09-04 19:02:49 +0200439 max_ev = len(self._fd_to_key)
440 ready = []
441 try:
442 fd_event_list = self._epoll.poll(timeout, max_ev)
443 except InterruptedError:
444 return ready
445 for fd, event in fd_event_list:
446 events = 0
447 if event & ~select.EPOLLIN:
448 events |= EVENT_WRITE
449 if event & ~select.EPOLLOUT:
450 events |= EVENT_READ
451
452 key = self._key_from_fd(fd)
453 if key:
454 ready.append((key, events & key.events))
455 return ready
456
457 def close(self):
Charles-François Natali243d8d82013-09-04 19:02:49 +0200458 self._epoll.close()
Guido van Rossum61a2ced2013-10-31 11:01:40 -0700459 super().close()
Charles-François Natali243d8d82013-09-04 19:02:49 +0200460
461
462if hasattr(select, 'kqueue'):
463
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100464 class KqueueSelector(_BaseSelectorImpl):
Charles-François Natali243d8d82013-09-04 19:02:49 +0200465 """Kqueue-based selector."""
466
467 def __init__(self):
468 super().__init__()
469 self._kqueue = select.kqueue()
470
Victor Stinner635fca92014-01-25 14:56:48 +0100471 @property
472 def resolution(self):
473 return 1e-9
474
Charles-François Natali243d8d82013-09-04 19:02:49 +0200475 def fileno(self):
476 return self._kqueue.fileno()
477
478 def register(self, fileobj, events, data=None):
479 key = super().register(fileobj, events, data)
480 if events & EVENT_READ:
481 kev = select.kevent(key.fd, select.KQ_FILTER_READ,
482 select.KQ_EV_ADD)
483 self._kqueue.control([kev], 0, 0)
484 if events & EVENT_WRITE:
485 kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
486 select.KQ_EV_ADD)
487 self._kqueue.control([kev], 0, 0)
488 return key
489
490 def unregister(self, fileobj):
491 key = super().unregister(fileobj)
492 if key.events & EVENT_READ:
493 kev = select.kevent(key.fd, select.KQ_FILTER_READ,
494 select.KQ_EV_DELETE)
Guido van Rossum9710ff02013-12-07 15:57:01 -0800495 try:
496 self._kqueue.control([kev], 0, 0)
497 except OSError:
498 # This can happen if the FD was closed since it
499 # was registered.
500 pass
Charles-François Natali243d8d82013-09-04 19:02:49 +0200501 if key.events & EVENT_WRITE:
502 kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
503 select.KQ_EV_DELETE)
Guido van Rossum9710ff02013-12-07 15:57:01 -0800504 try:
505 self._kqueue.control([kev], 0, 0)
506 except OSError:
507 # See comment above.
508 pass
Charles-François Natali243d8d82013-09-04 19:02:49 +0200509 return key
510
511 def select(self, timeout=None):
512 timeout = None if timeout is None else max(timeout, 0)
513 max_ev = len(self._fd_to_key)
514 ready = []
515 try:
516 kev_list = self._kqueue.control(None, max_ev, timeout)
517 except InterruptedError:
518 return ready
519 for kev in kev_list:
520 fd = kev.ident
521 flag = kev.filter
522 events = 0
523 if flag == select.KQ_FILTER_READ:
524 events |= EVENT_READ
525 if flag == select.KQ_FILTER_WRITE:
526 events |= EVENT_WRITE
527
528 key = self._key_from_fd(fd)
529 if key:
530 ready.append((key, events & key.events))
531 return ready
532
533 def close(self):
Charles-François Natali243d8d82013-09-04 19:02:49 +0200534 self._kqueue.close()
Guido van Rossum61a2ced2013-10-31 11:01:40 -0700535 super().close()
Charles-François Natali243d8d82013-09-04 19:02:49 +0200536
537
538# Choose the best implementation: roughly, epoll|kqueue > poll > select.
539# select() also can't accept a FD > FD_SETSIZE (usually around 1024)
540if 'KqueueSelector' in globals():
541 DefaultSelector = KqueueSelector
542elif 'EpollSelector' in globals():
543 DefaultSelector = EpollSelector
544elif 'PollSelector' in globals():
545 DefaultSelector = PollSelector
546else:
547 DefaultSelector = SelectSelector