pollreactor.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. A poll() 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 pollreactor
  8. pollreactor.install()
  9. """
  10. # System imports
  11. import errno
  12. from select import (
  13. POLLERR,
  14. POLLHUP,
  15. POLLIN,
  16. POLLNVAL,
  17. POLLOUT,
  18. error as SelectError,
  19. poll,
  20. )
  21. from zope.interface import implementer
  22. from twisted.internet import posixbase
  23. from twisted.internet.interfaces import IReactorFDSet
  24. # Twisted imports
  25. from twisted.python import log
  26. @implementer(IReactorFDSet)
  27. class PollReactor(posixbase.PosixReactorBase, posixbase._PollLikeMixin):
  28. """
  29. A reactor that uses poll(2).
  30. @ivar _poller: A L{select.poll} which will be used to check for I/O
  31. readiness.
  32. @ivar _selectables: A dictionary mapping integer file descriptors to
  33. instances of L{FileDescriptor} which have been registered with the
  34. reactor. All L{FileDescriptor}s which are currently receiving read or
  35. write readiness notifications will be present as values in this
  36. dictionary.
  37. @ivar _reads: A dictionary mapping integer file descriptors to arbitrary
  38. values (this is essentially a set). Keys in this dictionary will be
  39. registered with C{_poller} for read readiness notifications which will
  40. be dispatched to the corresponding L{FileDescriptor} instances in
  41. C{_selectables}.
  42. @ivar _writes: A dictionary mapping integer file descriptors to arbitrary
  43. values (this is essentially a set). Keys in this dictionary will be
  44. registered with C{_poller} for write readiness notifications which will
  45. be dispatched to the corresponding L{FileDescriptor} instances in
  46. C{_selectables}.
  47. """
  48. _POLL_DISCONNECTED = POLLHUP | POLLERR | POLLNVAL
  49. _POLL_IN = POLLIN
  50. _POLL_OUT = POLLOUT
  51. def __init__(self):
  52. """
  53. Initialize polling object, file descriptor tracking dictionaries, and
  54. the base class.
  55. """
  56. self._poller = poll()
  57. self._selectables = {}
  58. self._reads = {}
  59. self._writes = {}
  60. posixbase.PosixReactorBase.__init__(self)
  61. def _updateRegistration(self, fd):
  62. """Register/unregister an fd with the poller."""
  63. try:
  64. self._poller.unregister(fd)
  65. except KeyError:
  66. pass
  67. mask = 0
  68. if fd in self._reads:
  69. mask = mask | POLLIN
  70. if fd in self._writes:
  71. mask = mask | POLLOUT
  72. if mask != 0:
  73. self._poller.register(fd, mask)
  74. else:
  75. if fd in self._selectables:
  76. del self._selectables[fd]
  77. def _dictRemove(self, selectable, mdict):
  78. try:
  79. # the easy way
  80. fd = selectable.fileno()
  81. # make sure the fd is actually real. In some situations we can get
  82. # -1 here.
  83. mdict[fd]
  84. except BaseException:
  85. # the hard way: necessary because fileno() may disappear at any
  86. # moment, thanks to python's underlying sockets impl
  87. for fd, fdes in self._selectables.items():
  88. if selectable is fdes:
  89. break
  90. else:
  91. # Hmm, maybe not the right course of action? This method can't
  92. # fail, because it happens inside error detection...
  93. return
  94. if fd in mdict:
  95. del mdict[fd]
  96. self._updateRegistration(fd)
  97. def addReader(self, reader):
  98. """Add a FileDescriptor for notification of data available to read."""
  99. fd = reader.fileno()
  100. if fd not in self._reads:
  101. self._selectables[fd] = reader
  102. self._reads[fd] = 1
  103. self._updateRegistration(fd)
  104. def addWriter(self, writer):
  105. """Add a FileDescriptor for notification of data available to write."""
  106. fd = writer.fileno()
  107. if fd not in self._writes:
  108. self._selectables[fd] = writer
  109. self._writes[fd] = 1
  110. self._updateRegistration(fd)
  111. def removeReader(self, reader):
  112. """Remove a Selectable for notification of data available to read."""
  113. return self._dictRemove(reader, self._reads)
  114. def removeWriter(self, writer):
  115. """Remove a Selectable for notification of data available to write."""
  116. return self._dictRemove(writer, self._writes)
  117. def removeAll(self):
  118. """
  119. Remove all selectables, and return a list of them.
  120. """
  121. return self._removeAll(
  122. [self._selectables[fd] for fd in self._reads],
  123. [self._selectables[fd] for fd in self._writes],
  124. )
  125. def doPoll(self, timeout):
  126. """Poll the poller for new events."""
  127. if timeout is not None:
  128. timeout = int(timeout * 1000) # convert seconds to milliseconds
  129. try:
  130. l = self._poller.poll(timeout)
  131. except SelectError as e:
  132. if e.args[0] == errno.EINTR:
  133. return
  134. else:
  135. raise
  136. _drdw = self._doReadOrWrite
  137. for fd, event in l:
  138. try:
  139. selectable = self._selectables[fd]
  140. except KeyError:
  141. # Handles the infrequent case where one selectable's
  142. # handler disconnects another.
  143. continue
  144. log.callWithLogger(selectable, _drdw, selectable, fd, event)
  145. doIteration = doPoll
  146. def getReaders(self):
  147. return [self._selectables[fd] for fd in self._reads]
  148. def getWriters(self):
  149. return [self._selectables[fd] for fd in self._writes]
  150. def install():
  151. """Install the poll() reactor."""
  152. p = PollReactor()
  153. from twisted.internet.main import installReactor
  154. installReactor(p)
  155. __all__ = ["PollReactor", "install"]