1
# -*- test-case-name: twisted.test.test_internet -*-
2
# Copyright (c) 2001-2007 Twisted Matrix Laboratories.
3
# See LICENSE for details.
9
Maintainer: Itamar Shtull-Trauring
12
from time import sleep
15
from errno import EINTR, EBADF
17
from zope.interface import implements
19
from twisted.internet.interfaces import IReactorFDSet
20
from twisted.internet import error
21
from twisted.internet import posixbase
22
from twisted.python import log
23
from twisted.python.runtime import platformType
26
def win32select(r, w, e, timeout=None):
27
"""Win32 select wrapper."""
29
# windows select() exits immediately when no sockets
33
timeout = min(timeout, 0.001)
36
# windows doesn't process 'signals' inside select(), so we set a max
37
# time or ctrl-c will never be recognized
38
if timeout is None or timeout > 0.5:
40
r, w, e = select.select(r, w, w, timeout)
43
if platformType == "win32":
46
_select = select.select
48
# Exceptions that doSelect might return frequently
49
_NO_FILENO = error.ConnectionFdescWentAway('Handler has no fileno method')
50
_NO_FILEDESC = error.ConnectionFdescWentAway('Filedescriptor went away')
52
class SelectReactor(posixbase.PosixReactorBase):
54
A select() based reactor - runs on all POSIX platforms and on Win32.
56
@ivar _reads: A dictionary mapping L{FileDescriptor} instances to arbitrary
57
values (this is essentially a set). Keys in this dictionary will be
58
checked for read events.
60
@ivar _writes: A dictionary mapping L{FileDescriptor} instances to
61
arbitrary values (this is essentially a set). Keys in this dictionary
62
will be checked for writability.
64
implements(IReactorFDSet)
68
Initialize file descriptor tracking dictionaries and the base class.
72
posixbase.PosixReactorBase.__init__(self)
75
def _preenDescriptors(self):
76
log.msg("Malformed file descriptor found. Preening lists.")
77
readers = self._reads.keys()
78
writers = self._writes.keys()
81
for selDict, selList in ((self._reads, readers),
82
(self._writes, writers)):
83
for selectable in selList:
85
select.select([selectable], [selectable], [selectable], 0)
87
log.msg("bad descriptor %s" % selectable)
88
self._disconnectSelectable(selectable, e, False)
90
selDict[selectable] = 1
93
def doSelect(self, timeout):
95
Run one iteration of the I/O monitor loop.
97
This will run all selectables who had input or output readiness
102
r, w, ignored = _select(self._reads.keys(),
106
except ValueError, ve:
107
# Possibly a file descriptor has gone negative?
109
self._preenDescriptors()
110
except TypeError, te:
111
# Something *totally* invalid (object w/o fileno, non-integral
114
self._preenDescriptors()
115
except (select.error, IOError), se:
116
# select(2) encountered an error
117
if se.args[0] in (0, 2):
118
# windows does this if it got an empty list
119
if (not self._reads) and (not self._writes):
123
elif se.args[0] == EINTR:
125
elif se.args[0] == EBADF:
126
self._preenDescriptors()
128
# OK, I really don't know what's going on. Blow up.
130
_drdw = self._doReadOrWrite
131
_logrun = log.callWithLogger
132
for selectables, method, fdset in ((r, "doRead", self._reads),
133
(w,"doWrite", self._writes)):
134
for selectable in selectables:
135
# if this was disconnected in another thread, kill it.
136
# ^^^^ --- what the !@#*? serious! -exarkun
137
if selectable not in fdset:
139
# This for pausing input when we're not ready for more.
140
_logrun(selectable, _drdw, selectable, method, dict)
142
doIteration = doSelect
144
def _doReadOrWrite(self, selectable, method, dict):
146
why = getattr(selectable, method)()
147
handfn = getattr(selectable, 'fileno', None)
153
why = sys.exc_info()[1]
156
self._disconnectSelectable(selectable, why, method=="doRead")
158
def addReader(self, reader):
160
Add a FileDescriptor for notification of data available to read.
162
self._reads[reader] = 1
164
def addWriter(self, writer):
166
Add a FileDescriptor for notification of data available to write.
168
self._writes[writer] = 1
170
def removeReader(self, reader):
172
Remove a Selectable for notification of data available to read.
174
if reader in self._reads:
175
del self._reads[reader]
177
def removeWriter(self, writer):
179
Remove a Selectable for notification of data available to write.
181
if writer in self._writes:
182
del self._writes[writer]
185
return self._removeAll(self._reads, self._writes)
188
def getReaders(self):
189
return self._reads.keys()
192
def getWriters(self):
193
return self._writes.keys()
198
"""Configure the twisted mainloop to be run using the select() reactor.
200
reactor = SelectReactor()
201
from twisted.internet.main import installReactor
202
installReactor(reactor)
204
__all__ = ['install']