You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

epollreactor.py 8.3KB

5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. An epoll() based implementation of the twisted main loop.
  5. To install the event loop (and you should do this before any connections,
  6. listeners or connectors are added)::
  7. from twisted.internet import epollreactor
  8. epollreactor.install()
  9. """
  10. from __future__ import division, absolute_import
  11. from select import epoll, EPOLLHUP, EPOLLERR, EPOLLIN, EPOLLOUT
  12. import errno
  13. from zope.interface import implementer
  14. from twisted.internet.interfaces import IReactorFDSet
  15. from twisted.python import log
  16. from twisted.internet import posixbase
  17. @implementer(IReactorFDSet)
  18. class EPollReactor(posixbase.PosixReactorBase, posixbase._PollLikeMixin):
  19. """
  20. A reactor that uses epoll(7).
  21. @ivar _poller: A C{epoll} which will be used to check for I/O
  22. readiness.
  23. @ivar _selectables: A dictionary mapping integer file descriptors to
  24. instances of C{FileDescriptor} which have been registered with the
  25. reactor. All C{FileDescriptors} which are currently receiving read or
  26. write readiness notifications will be present as values in this
  27. dictionary.
  28. @ivar _reads: A set containing integer file descriptors. Values in this
  29. set will be registered with C{_poller} for read readiness notifications
  30. which will be dispatched to the corresponding C{FileDescriptor}
  31. instances in C{_selectables}.
  32. @ivar _writes: A set containing integer file descriptors. Values in this
  33. set will be registered with C{_poller} for write readiness
  34. notifications which will be dispatched to the corresponding
  35. C{FileDescriptor} instances in C{_selectables}.
  36. @ivar _continuousPolling: A L{_ContinuousPolling} instance, used to handle
  37. file descriptors (e.g. filesystem files) that are not supported by
  38. C{epoll(7)}.
  39. """
  40. # Attributes for _PollLikeMixin
  41. _POLL_DISCONNECTED = (EPOLLHUP | EPOLLERR)
  42. _POLL_IN = EPOLLIN
  43. _POLL_OUT = EPOLLOUT
  44. def __init__(self):
  45. """
  46. Initialize epoll object, file descriptor tracking dictionaries, and the
  47. base class.
  48. """
  49. # Create the poller we're going to use. The 1024 here is just a hint
  50. # to the kernel, it is not a hard maximum. After Linux 2.6.8, the size
  51. # argument is completely ignored.
  52. self._poller = epoll(1024)
  53. self._reads = set()
  54. self._writes = set()
  55. self._selectables = {}
  56. self._continuousPolling = posixbase._ContinuousPolling(self)
  57. posixbase.PosixReactorBase.__init__(self)
  58. def _add(self, xer, primary, other, selectables, event, antievent):
  59. """
  60. Private method for adding a descriptor from the event loop.
  61. It takes care of adding it if new or modifying it if already added
  62. for another state (read -> read/write for example).
  63. """
  64. fd = xer.fileno()
  65. if fd not in primary:
  66. flags = event
  67. # epoll_ctl can raise all kinds of IOErrors, and every one
  68. # indicates a bug either in the reactor or application-code.
  69. # Let them all through so someone sees a traceback and fixes
  70. # something. We'll do the same thing for every other call to
  71. # this method in this file.
  72. if fd in other:
  73. flags |= antievent
  74. self._poller.modify(fd, flags)
  75. else:
  76. self._poller.register(fd, flags)
  77. # Update our own tracking state *only* after the epoll call has
  78. # succeeded. Otherwise we may get out of sync.
  79. primary.add(fd)
  80. selectables[fd] = xer
  81. def addReader(self, reader):
  82. """
  83. Add a FileDescriptor for notification of data available to read.
  84. """
  85. try:
  86. self._add(reader, self._reads, self._writes, self._selectables,
  87. EPOLLIN, EPOLLOUT)
  88. except IOError as e:
  89. if e.errno == errno.EPERM:
  90. # epoll(7) doesn't support certain file descriptors,
  91. # e.g. filesystem files, so for those we just poll
  92. # continuously:
  93. self._continuousPolling.addReader(reader)
  94. else:
  95. raise
  96. def addWriter(self, writer):
  97. """
  98. Add a FileDescriptor for notification of data available to write.
  99. """
  100. try:
  101. self._add(writer, self._writes, self._reads, self._selectables,
  102. EPOLLOUT, EPOLLIN)
  103. except IOError as e:
  104. if e.errno == errno.EPERM:
  105. # epoll(7) doesn't support certain file descriptors,
  106. # e.g. filesystem files, so for those we just poll
  107. # continuously:
  108. self._continuousPolling.addWriter(writer)
  109. else:
  110. raise
  111. def _remove(self, xer, primary, other, selectables, event, antievent):
  112. """
  113. Private method for removing a descriptor from the event loop.
  114. It does the inverse job of _add, and also add a check in case of the fd
  115. has gone away.
  116. """
  117. fd = xer.fileno()
  118. if fd == -1:
  119. for fd, fdes in selectables.items():
  120. if xer is fdes:
  121. break
  122. else:
  123. return
  124. if fd in primary:
  125. if fd in other:
  126. flags = antievent
  127. # See comment above modify call in _add.
  128. self._poller.modify(fd, flags)
  129. else:
  130. del selectables[fd]
  131. # See comment above _control call in _add.
  132. self._poller.unregister(fd)
  133. primary.remove(fd)
  134. def removeReader(self, reader):
  135. """
  136. Remove a Selectable for notification of data available to read.
  137. """
  138. if self._continuousPolling.isReading(reader):
  139. self._continuousPolling.removeReader(reader)
  140. return
  141. self._remove(reader, self._reads, self._writes, self._selectables,
  142. EPOLLIN, EPOLLOUT)
  143. def removeWriter(self, writer):
  144. """
  145. Remove a Selectable for notification of data available to write.
  146. """
  147. if self._continuousPolling.isWriting(writer):
  148. self._continuousPolling.removeWriter(writer)
  149. return
  150. self._remove(writer, self._writes, self._reads, self._selectables,
  151. EPOLLOUT, EPOLLIN)
  152. def removeAll(self):
  153. """
  154. Remove all selectables, and return a list of them.
  155. """
  156. return (self._removeAll(
  157. [self._selectables[fd] for fd in self._reads],
  158. [self._selectables[fd] for fd in self._writes]) +
  159. self._continuousPolling.removeAll())
  160. def getReaders(self):
  161. return ([self._selectables[fd] for fd in self._reads] +
  162. self._continuousPolling.getReaders())
  163. def getWriters(self):
  164. return ([self._selectables[fd] for fd in self._writes] +
  165. self._continuousPolling.getWriters())
  166. def doPoll(self, timeout):
  167. """
  168. Poll the poller for new events.
  169. """
  170. if timeout is None:
  171. timeout = -1 # Wait indefinitely.
  172. try:
  173. # Limit the number of events to the number of io objects we're
  174. # currently tracking (because that's maybe a good heuristic) and
  175. # the amount of time we block to the value specified by our
  176. # caller.
  177. l = self._poller.poll(timeout, len(self._selectables))
  178. except IOError as err:
  179. if err.errno == errno.EINTR:
  180. return
  181. # See epoll_wait(2) for documentation on the other conditions
  182. # under which this can fail. They can only be due to a serious
  183. # programming error on our part, so let's just announce them
  184. # loudly.
  185. raise
  186. _drdw = self._doReadOrWrite
  187. for fd, event in l:
  188. try:
  189. selectable = self._selectables[fd]
  190. except KeyError:
  191. pass
  192. else:
  193. log.callWithLogger(selectable, _drdw, selectable, fd, event)
  194. doIteration = doPoll
  195. def install():
  196. """
  197. Install the epoll() reactor.
  198. """
  199. p = EPollReactor()
  200. from twisted.internet.main import installReactor
  201. installReactor(p)
  202. __all__ = ["EPollReactor", "install"]