blob: cd8b29e4cc2c246a63e901f51cf566172efb1574 [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
11import select
12import sys
13
14
15# generic events, that must be mapped to implementation-specific ones
16EVENT_READ = (1 << 0)
17EVENT_WRITE = (1 << 1)
18
19
20def _fileobj_to_fd(fileobj):
21 """Return a file descriptor from a file object.
22
23 Parameters:
24 fileobj -- file object or file descriptor
25
26 Returns:
27 corresponding file descriptor
Guido van Rossum9710ff02013-12-07 15:57:01 -080028
29 Raises:
30 ValueError if the object is invalid
Charles-François Natali243d8d82013-09-04 19:02:49 +020031 """
32 if isinstance(fileobj, int):
33 fd = fileobj
34 else:
35 try:
36 fd = int(fileobj.fileno())
37 except (AttributeError, TypeError, ValueError):
38 raise ValueError("Invalid file object: "
39 "{!r}".format(fileobj)) from None
40 if fd < 0:
41 raise ValueError("Invalid file descriptor: {}".format(fd))
42 return fd
43
44
45SelectorKey = namedtuple('SelectorKey', ['fileobj', 'fd', 'events', 'data'])
46"""Object used to associate a file object to its backing file descriptor,
47selected event mask and attached data."""
48
49
Charles-François Natali4574b492013-10-30 20:31:04 +010050class _SelectorMapping(Mapping):
51 """Mapping of file objects to selector keys."""
52
53 def __init__(self, selector):
54 self._selector = selector
55
56 def __len__(self):
57 return len(self._selector._fd_to_key)
58
59 def __getitem__(self, fileobj):
60 try:
Guido van Rossum9710ff02013-12-07 15:57:01 -080061 fd = self._selector._fileobj_lookup(fileobj)
62 return self._selector._fd_to_key[fd]
Charles-François Natali4574b492013-10-30 20:31:04 +010063 except KeyError:
64 raise KeyError("{!r} is not registered".format(fileobj)) from None
65
66 def __iter__(self):
67 return iter(self._selector._fd_to_key)
68
69
Charles-François Natali243d8d82013-09-04 19:02:49 +020070class BaseSelector(metaclass=ABCMeta):
Charles-François Natalib3330a0a2013-12-01 11:04:17 +010071 """Selector abstract base class.
Charles-François Natali243d8d82013-09-04 19:02:49 +020072
73 A selector supports registering file objects to be monitored for specific
74 I/O events.
75
76 A file object is a file descriptor or any object with a `fileno()` method.
77 An arbitrary object can be attached to the file object, which can be used
78 for example to store context information, a callback, etc.
79
80 A selector can use various implementations (select(), poll(), epoll()...)
81 depending on the platform. The default `Selector` class uses the most
82 performant implementation on the current platform.
83 """
84
Charles-François Natalib3330a0a2013-12-01 11:04:17 +010085 @abstractmethod
Charles-François Natali243d8d82013-09-04 19:02:49 +020086 def register(self, fileobj, events, data=None):
87 """Register a file object.
88
89 Parameters:
90 fileobj -- file object or file descriptor
91 events -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE)
92 data -- attached data
93
94 Returns:
95 SelectorKey instance
Guido van Rossum9710ff02013-12-07 15:57:01 -080096
97 Raises:
98 ValueError if events is invalid
99 KeyError if fileobj is already registered
100 OSError if fileobj is closed or otherwise is unacceptable to
101 the underlying system call (if a system call is made)
102
103 Note:
104 OSError may or may not be raised
Charles-François Natali243d8d82013-09-04 19:02:49 +0200105 """
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100106 raise NotImplementedError
Charles-François Natali243d8d82013-09-04 19:02:49 +0200107
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100108 @abstractmethod
Charles-François Natali243d8d82013-09-04 19:02:49 +0200109 def unregister(self, fileobj):
110 """Unregister a file object.
111
112 Parameters:
113 fileobj -- file object or file descriptor
114
115 Returns:
116 SelectorKey instance
Guido van Rossum9710ff02013-12-07 15:57:01 -0800117
118 Raises:
119 KeyError if fileobj is not registered
120
121 Note:
122 If fileobj is registered but has since been closed this does
123 *not* raise OSError (even if the wrapped syscall does)
Charles-François Natali243d8d82013-09-04 19:02:49 +0200124 """
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100125 raise NotImplementedError
Charles-François Natali243d8d82013-09-04 19:02:49 +0200126
127 def modify(self, fileobj, events, data=None):
128 """Change a registered file object monitored events or attached data.
129
130 Parameters:
131 fileobj -- file object or file descriptor
132 events -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE)
133 data -- attached data
134
135 Returns:
136 SelectorKey instance
Guido van Rossum9710ff02013-12-07 15:57:01 -0800137
138 Raises:
139 Anything that unregister() or register() raises
Charles-François Natali243d8d82013-09-04 19:02:49 +0200140 """
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100141 self.unregister(fileobj)
142 return self.register(fileobj, events, data)
Charles-François Natali243d8d82013-09-04 19:02:49 +0200143
144 @abstractmethod
145 def select(self, timeout=None):
146 """Perform the actual selection, until some monitored file objects are
147 ready or a timeout expires.
148
149 Parameters:
150 timeout -- if timeout > 0, this specifies the maximum wait time, in
151 seconds
152 if timeout <= 0, the select() call won't block, and will
153 report the currently ready file objects
154 if timeout is None, select() will block until a monitored
155 file object becomes ready
156
157 Returns:
158 list of (key, events) for ready file objects
159 `events` is a bitwise mask of EVENT_READ|EVENT_WRITE
160 """
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100161 raise NotImplementedError
Charles-François Natali243d8d82013-09-04 19:02:49 +0200162
163 def close(self):
164 """Close the selector.
165
166 This must be called to make sure that any underlying resource is freed.
167 """
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100168 pass
Charles-François Natali243d8d82013-09-04 19:02:49 +0200169
170 def get_key(self, fileobj):
171 """Return the key associated to a registered file object.
172
173 Returns:
174 SelectorKey for this file object
175 """
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100176 mapping = self.get_map()
Charles-François Natali243d8d82013-09-04 19:02:49 +0200177 try:
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100178 return mapping[fileobj]
Charles-François Natali243d8d82013-09-04 19:02:49 +0200179 except KeyError:
180 raise KeyError("{!r} is not registered".format(fileobj)) from None
181
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100182 @abstractmethod
Charles-François Natali4574b492013-10-30 20:31:04 +0100183 def get_map(self):
184 """Return a mapping of file objects to selector keys."""
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100185 raise NotImplementedError
Charles-François Natali4574b492013-10-30 20:31:04 +0100186
Charles-François Natali243d8d82013-09-04 19:02:49 +0200187 def __enter__(self):
188 return self
189
190 def __exit__(self, *args):
191 self.close()
192
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100193
194class _BaseSelectorImpl(BaseSelector):
195 """Base selector implementation."""
196
197 def __init__(self):
198 # this maps file descriptors to keys
199 self._fd_to_key = {}
200 # read-only mapping returned by get_map()
201 self._map = _SelectorMapping(self)
202
Guido van Rossum9710ff02013-12-07 15:57:01 -0800203 def _fileobj_lookup(self, fileobj):
204 """Return a file descriptor from a file object.
205
206 This wraps _fileobj_to_fd() to do an exhaustive search in case
207 the object is invalid but we still have it in our map. This
208 is used by unregister() so we can unregister an object that
209 was previously registered even if it is closed. It is also
210 used by _SelectorMapping.
211 """
212 try:
213 return _fileobj_to_fd(fileobj)
214 except ValueError:
215 # Do an exhaustive search.
216 for key in self._fd_to_key.values():
217 if key.fileobj is fileobj:
218 return key.fd
219 # Raise ValueError after all.
220 raise
221
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100222 def register(self, fileobj, events, data=None):
223 if (not events) or (events & ~(EVENT_READ | EVENT_WRITE)):
224 raise ValueError("Invalid events: {!r}".format(events))
225
Guido van Rossum9710ff02013-12-07 15:57:01 -0800226 key = SelectorKey(fileobj, self._fileobj_lookup(fileobj), events, data)
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100227
228 if key.fd in self._fd_to_key:
Guido van Rossum9710ff02013-12-07 15:57:01 -0800229 raise KeyError("{!r} (FD {}) is already registered"
230 .format(fileobj, key.fd))
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100231
232 self._fd_to_key[key.fd] = key
233 return key
234
235 def unregister(self, fileobj):
236 try:
Guido van Rossum9710ff02013-12-07 15:57:01 -0800237 key = self._fd_to_key.pop(self._fileobj_lookup(fileobj))
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100238 except KeyError:
239 raise KeyError("{!r} is not registered".format(fileobj)) from None
240 return key
241
242 def modify(self, fileobj, events, data=None):
243 # TODO: Subclasses can probably optimize this even further.
244 try:
Guido van Rossum9710ff02013-12-07 15:57:01 -0800245 key = self._fd_to_key[self._fileobj_lookup(fileobj)]
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100246 except KeyError:
247 raise KeyError("{!r} is not registered".format(fileobj)) from None
248 if events != key.events:
249 self.unregister(fileobj)
250 key = self.register(fileobj, events, data)
251 elif data != key.data:
252 # Use a shortcut to update the data.
253 key = key._replace(data=data)
254 self._fd_to_key[key.fd] = key
255 return key
256
257 def close(self):
258 self._fd_to_key.clear()
259
260 def get_map(self):
261 return self._map
262
Charles-François Natali243d8d82013-09-04 19:02:49 +0200263 def _key_from_fd(self, fd):
264 """Return the key associated to a given file descriptor.
265
266 Parameters:
267 fd -- file descriptor
268
269 Returns:
270 corresponding key, or None if not found
271 """
272 try:
273 return self._fd_to_key[fd]
274 except KeyError:
275 return None
276
277
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100278class SelectSelector(_BaseSelectorImpl):
Charles-François Natali243d8d82013-09-04 19:02:49 +0200279 """Select-based selector."""
280
281 def __init__(self):
282 super().__init__()
283 self._readers = set()
284 self._writers = set()
285
286 def register(self, fileobj, events, data=None):
287 key = super().register(fileobj, events, data)
288 if events & EVENT_READ:
289 self._readers.add(key.fd)
290 if events & EVENT_WRITE:
291 self._writers.add(key.fd)
292 return key
293
294 def unregister(self, fileobj):
295 key = super().unregister(fileobj)
296 self._readers.discard(key.fd)
297 self._writers.discard(key.fd)
298 return key
299
300 if sys.platform == 'win32':
301 def _select(self, r, w, _, timeout=None):
302 r, w, x = select.select(r, w, w, timeout)
303 return r, w + x, []
304 else:
305 _select = select.select
306
307 def select(self, timeout=None):
308 timeout = None if timeout is None else max(timeout, 0)
309 ready = []
310 try:
311 r, w, _ = self._select(self._readers, self._writers, [], timeout)
312 except InterruptedError:
313 return ready
314 r = set(r)
315 w = set(w)
316 for fd in r | w:
317 events = 0
318 if fd in r:
319 events |= EVENT_READ
320 if fd in w:
321 events |= EVENT_WRITE
322
323 key = self._key_from_fd(fd)
324 if key:
325 ready.append((key, events & key.events))
326 return ready
327
328
329if hasattr(select, 'poll'):
330
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100331 class PollSelector(_BaseSelectorImpl):
Charles-François Natali243d8d82013-09-04 19:02:49 +0200332 """Poll-based selector."""
333
334 def __init__(self):
335 super().__init__()
336 self._poll = select.poll()
337
338 def register(self, fileobj, events, data=None):
339 key = super().register(fileobj, events, data)
340 poll_events = 0
341 if events & EVENT_READ:
342 poll_events |= select.POLLIN
343 if events & EVENT_WRITE:
344 poll_events |= select.POLLOUT
345 self._poll.register(key.fd, poll_events)
346 return key
347
348 def unregister(self, fileobj):
349 key = super().unregister(fileobj)
350 self._poll.unregister(key.fd)
351 return key
352
353 def select(self, timeout=None):
Victor Stinner11da8e22014-01-21 01:48:28 +0100354 if timeout is None:
355 timeout = None
Victor Stinner7067b5d2014-01-21 17:49:41 +0100356 elif timeout <= 0:
Victor Stinner11da8e22014-01-21 01:48:28 +0100357 timeout = 0
358 else:
Victor Stinner20418592014-01-25 14:43:45 +0100359 # Round towards zero
360 timeout = int(timeout * 1000)
Charles-François Natali243d8d82013-09-04 19:02:49 +0200361 ready = []
362 try:
363 fd_event_list = self._poll.poll(timeout)
364 except InterruptedError:
365 return ready
366 for fd, event in fd_event_list:
367 events = 0
368 if event & ~select.POLLIN:
369 events |= EVENT_WRITE
370 if event & ~select.POLLOUT:
371 events |= EVENT_READ
372
373 key = self._key_from_fd(fd)
374 if key:
375 ready.append((key, events & key.events))
376 return ready
377
378
379if hasattr(select, 'epoll'):
380
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100381 class EpollSelector(_BaseSelectorImpl):
Charles-François Natali243d8d82013-09-04 19:02:49 +0200382 """Epoll-based selector."""
383
384 def __init__(self):
385 super().__init__()
386 self._epoll = select.epoll()
387
388 def fileno(self):
389 return self._epoll.fileno()
390
391 def register(self, fileobj, events, data=None):
392 key = super().register(fileobj, events, data)
393 epoll_events = 0
394 if events & EVENT_READ:
395 epoll_events |= select.EPOLLIN
396 if events & EVENT_WRITE:
397 epoll_events |= select.EPOLLOUT
398 self._epoll.register(key.fd, epoll_events)
399 return key
400
401 def unregister(self, fileobj):
402 key = super().unregister(fileobj)
Guido van Rossum9710ff02013-12-07 15:57:01 -0800403 try:
404 self._epoll.unregister(key.fd)
405 except OSError:
406 # This can happen if the FD was closed since it
407 # was registered.
408 pass
Charles-François Natali243d8d82013-09-04 19:02:49 +0200409 return key
410
411 def select(self, timeout=None):
Victor Stinner567b26e2014-01-21 21:00:47 +0100412 if timeout is None:
413 timeout = -1
414 elif timeout <= 0:
415 timeout = 0
Charles-François Natali243d8d82013-09-04 19:02:49 +0200416 max_ev = len(self._fd_to_key)
417 ready = []
418 try:
419 fd_event_list = self._epoll.poll(timeout, max_ev)
420 except InterruptedError:
421 return ready
422 for fd, event in fd_event_list:
423 events = 0
424 if event & ~select.EPOLLIN:
425 events |= EVENT_WRITE
426 if event & ~select.EPOLLOUT:
427 events |= EVENT_READ
428
429 key = self._key_from_fd(fd)
430 if key:
431 ready.append((key, events & key.events))
432 return ready
433
434 def close(self):
Charles-François Natali243d8d82013-09-04 19:02:49 +0200435 self._epoll.close()
Guido van Rossum61a2ced2013-10-31 11:01:40 -0700436 super().close()
Charles-François Natali243d8d82013-09-04 19:02:49 +0200437
438
439if hasattr(select, 'kqueue'):
440
Charles-François Natalib3330a0a2013-12-01 11:04:17 +0100441 class KqueueSelector(_BaseSelectorImpl):
Charles-François Natali243d8d82013-09-04 19:02:49 +0200442 """Kqueue-based selector."""
443
444 def __init__(self):
445 super().__init__()
446 self._kqueue = select.kqueue()
447
448 def fileno(self):
449 return self._kqueue.fileno()
450
451 def register(self, fileobj, events, data=None):
452 key = super().register(fileobj, events, data)
453 if events & EVENT_READ:
454 kev = select.kevent(key.fd, select.KQ_FILTER_READ,
455 select.KQ_EV_ADD)
456 self._kqueue.control([kev], 0, 0)
457 if events & EVENT_WRITE:
458 kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
459 select.KQ_EV_ADD)
460 self._kqueue.control([kev], 0, 0)
461 return key
462
463 def unregister(self, fileobj):
464 key = super().unregister(fileobj)
465 if key.events & EVENT_READ:
466 kev = select.kevent(key.fd, select.KQ_FILTER_READ,
467 select.KQ_EV_DELETE)
Guido van Rossum9710ff02013-12-07 15:57:01 -0800468 try:
469 self._kqueue.control([kev], 0, 0)
470 except OSError:
471 # This can happen if the FD was closed since it
472 # was registered.
473 pass
Charles-François Natali243d8d82013-09-04 19:02:49 +0200474 if key.events & EVENT_WRITE:
475 kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
476 select.KQ_EV_DELETE)
Guido van Rossum9710ff02013-12-07 15:57:01 -0800477 try:
478 self._kqueue.control([kev], 0, 0)
479 except OSError:
480 # See comment above.
481 pass
Charles-François Natali243d8d82013-09-04 19:02:49 +0200482 return key
483
484 def select(self, timeout=None):
485 timeout = None if timeout is None else max(timeout, 0)
486 max_ev = len(self._fd_to_key)
487 ready = []
488 try:
489 kev_list = self._kqueue.control(None, max_ev, timeout)
490 except InterruptedError:
491 return ready
492 for kev in kev_list:
493 fd = kev.ident
494 flag = kev.filter
495 events = 0
496 if flag == select.KQ_FILTER_READ:
497 events |= EVENT_READ
498 if flag == select.KQ_FILTER_WRITE:
499 events |= EVENT_WRITE
500
501 key = self._key_from_fd(fd)
502 if key:
503 ready.append((key, events & key.events))
504 return ready
505
506 def close(self):
Charles-François Natali243d8d82013-09-04 19:02:49 +0200507 self._kqueue.close()
Guido van Rossum61a2ced2013-10-31 11:01:40 -0700508 super().close()
Charles-François Natali243d8d82013-09-04 19:02:49 +0200509
510
511# Choose the best implementation: roughly, epoll|kqueue > poll > select.
512# select() also can't accept a FD > FD_SETSIZE (usually around 1024)
513if 'KqueueSelector' in globals():
514 DefaultSelector = KqueueSelector
515elif 'EpollSelector' in globals():
516 DefaultSelector = EpollSelector
517elif 'PollSelector' in globals():
518 DefaultSelector = PollSelector
519else:
520 DefaultSelector = SelectSelector