1 # -*- test-case-name: twisted.test.test_internet -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
10 from time import sleep
11 import sys, select, socket
12 from errno import EINTR, EBADF
14 from zope.interface import implements
16 from twisted.internet.interfaces import IReactorFDSet
17 from twisted.internet import posixbase
18 from twisted.python import log
19 from twisted.python.runtime import platformType
22 def win32select(r, w, e, timeout=None):
23 """Win32 select wrapper."""
25 # windows select() exits immediately when no sockets
29 timeout = min(timeout, 0.001)
32 # windows doesn't process 'signals' inside select(), so we set a max
33 # time or ctrl-c will never be recognized
34 if timeout is None or timeout > 0.5:
36 r, w, e = select.select(r, w, w, timeout)
39 if platformType == "win32":
42 _select = select.select
46 from twisted.internet.win32eventreactor import _ThreadedWin32EventsMixin
50 _extraBase = _ThreadedWin32EventsMixin
53 class SelectReactor(posixbase.PosixReactorBase, _extraBase):
55 A select() based reactor - runs on all POSIX platforms and on Win32.
57 @ivar _reads: A dictionary mapping L{FileDescriptor} instances to arbitrary
58 values (this is essentially a set). Keys in this dictionary will be
59 checked for read events.
61 @ivar _writes: A dictionary mapping L{FileDescriptor} instances to
62 arbitrary values (this is essentially a set). Keys in this dictionary
63 will be checked for writability.
65 implements(IReactorFDSet)
69 Initialize file descriptor tracking dictionaries and the base class.
73 posixbase.PosixReactorBase.__init__(self)
76 def _preenDescriptors(self):
77 log.msg("Malformed file descriptor found. Preening lists.")
78 readers = self._reads.keys()
79 writers = self._writes.keys()
82 for selDict, selList in ((self._reads, readers),
83 (self._writes, writers)):
84 for selectable in selList:
86 select.select([selectable], [selectable], [selectable], 0)
88 log.msg("bad descriptor %s" % selectable)
89 self._disconnectSelectable(selectable, e, False)
91 selDict[selectable] = 1
94 def doSelect(self, timeout):
96 Run one iteration of the I/O monitor loop.
98 This will run all selectables who had input or output readiness
102 r, w, ignored = _select(self._reads.keys(),
106 # Possibly a file descriptor has gone negative?
107 self._preenDescriptors()
110 # Something *totally* invalid (object w/o fileno, non-integral
113 self._preenDescriptors()
115 except (select.error, socket.error, IOError), se:
116 # select(2) encountered an error, perhaps while calling the fileno()
117 # method of a socket. (Python 2.6 socket.error is an IOError
118 # subclass, but on Python 2.5 and earlier it is not.)
119 if se.args[0] in (0, 2):
120 # windows does this if it got an empty list
121 if (not self._reads) and (not self._writes):
125 elif se.args[0] == EINTR:
127 elif se.args[0] == EBADF:
128 self._preenDescriptors()
131 # OK, I really don't know what's going on. Blow up.
134 _drdw = self._doReadOrWrite
135 _logrun = log.callWithLogger
136 for selectables, method, fdset in ((r, "doRead", self._reads),
137 (w,"doWrite", self._writes)):
138 for selectable in selectables:
139 # if this was disconnected in another thread, kill it.
140 # ^^^^ --- what the !@#*? serious! -exarkun
141 if selectable not in fdset:
143 # This for pausing input when we're not ready for more.
144 _logrun(selectable, _drdw, selectable, method, dict)
146 doIteration = doSelect
148 def _doReadOrWrite(self, selectable, method, dict):
150 why = getattr(selectable, method)()
152 why = sys.exc_info()[1]
155 self._disconnectSelectable(selectable, why, method=="doRead")
157 def addReader(self, reader):
159 Add a FileDescriptor for notification of data available to read.
161 self._reads[reader] = 1
163 def addWriter(self, writer):
165 Add a FileDescriptor for notification of data available to write.
167 self._writes[writer] = 1
169 def removeReader(self, reader):
171 Remove a Selectable for notification of data available to read.
173 if reader in self._reads:
174 del self._reads[reader]
176 def removeWriter(self, writer):
178 Remove a Selectable for notification of data available to write.
180 if writer in self._writes:
181 del self._writes[writer]
184 return self._removeAll(self._reads, self._writes)
187 def getReaders(self):
188 return self._reads.keys()
191 def getWriters(self):
192 return self._writes.keys()
197 """Configure the twisted mainloop to be run using the select() reactor.
199 reactor = SelectReactor()
200 from twisted.internet.main import installReactor
201 installReactor(reactor)
203 __all__ = ['install']