root / trunk / twisted / internet / selectreactor.py

Revision 24441, 6.5 kB (checked in by thijs, 1 year ago)

Merge maintainer-email-2438: Get rid of references to maintainer email addresses from code.

Author: thijs
Reviewer: exarkun
Fixes: #2438

Line 
1 # -*- test-case-name: twisted.test.test_internet -*-
2 # Copyright (c) 2001-2007 Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5
6 """
7 Select reactor
8
9 Maintainer: Itamar Shtull-Trauring
10 """
11
12 from time import sleep
13 import sys
14 import select
15 from errno import EINTR, EBADF
16
17 from zope.interface import implements
18
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
24
25
26 def win32select(r, w, e, timeout=None):
27     """Win32 select wrapper."""
28     if not (r or w):
29         # windows select() exits immediately when no sockets
30         if timeout is None:
31             timeout = 0.01
32         else:
33             timeout = min(timeout, 0.001)
34         sleep(timeout)
35         return [], [], []
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:
39         timeout = 0.5
40     r, w, e = select.select(r, w, w, timeout)
41     return r, w + e, []
42
43 if platformType == "win32":
44     _select = win32select
45 else:
46     _select = select.select
47
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')
51
52 class SelectReactor(posixbase.PosixReactorBase):
53     """
54     A select() based reactor - runs on all POSIX platforms and on Win32.
55
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.
59
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.
63     """
64     implements(IReactorFDSet)
65
66     def __init__(self):
67         """
68         Initialize file descriptor tracking dictionaries and the base class.
69         """
70         self._reads = {}
71         self._writes = {}
72         posixbase.PosixReactorBase.__init__(self)
73
74
75     def _preenDescriptors(self):
76         log.msg("Malformed file descriptor found.  Preening lists.")
77         readers = self._reads.keys()
78         writers = self._writes.keys()
79         self._reads.clear()
80         self._writes.clear()
81         for selDict, selList in ((self._reads, readers),
82                                  (self._writes, writers)):
83             for selectable in selList:
84                 try:
85                     select.select([selectable], [selectable], [selectable], 0)
86                 except Exception, e:
87                     log.msg("bad descriptor %s" % selectable)
88                     self._disconnectSelectable(selectable, e, False)
89                 else:
90                     selDict[selectable] = 1
91
92
93     def doSelect(self, timeout):
94         """
95         Run one iteration of the I/O monitor loop.
96
97         This will run all selectables who had input or output readiness
98         waiting for them.
99         """
100         while 1:
101             try:
102                 r, w, ignored = _select(self._reads.keys(),
103                                         self._writes.keys(),
104                                         [], timeout)
105                 break
106             except ValueError, ve:
107                 # Possibly a file descriptor has gone negative?
108                 log.err()
109                 self._preenDescriptors()
110             except TypeError, te:
111                 # Something *totally* invalid (object w/o fileno, non-integral
112                 # result) was passed
113                 log.err()
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):
120                         return
121                     else:
122                         raise
123                 elif se.args[0] == EINTR:
124                     return
125                 elif se.args[0] == EBADF:
126                     self._preenDescriptors()
127                 else:
128                     # OK, I really don't know what's going on.  Blow up.
129                     raise
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:
138                     continue
139                 # This for pausing input when we're not ready for more.
140                 _logrun(selectable, _drdw, selectable, method, dict)
141
142     doIteration = doSelect
143
144     def _doReadOrWrite(self, selectable, method, dict):
145         try:
146             why = getattr(selectable, method)()
147             handfn = getattr(selectable, 'fileno', None)
148             if not handfn:
149                 why = _NO_FILENO
150             elif handfn() == -1:
151                 why = _NO_FILEDESC
152         except:
153             why = sys.exc_info()[1]
154             log.err()
155         if why:
156             self._disconnectSelectable(selectable, why, method=="doRead")
157
158     def addReader(self, reader):
159         """
160         Add a FileDescriptor for notification of data available to read.
161         """
162         self._reads[reader] = 1
163
164     def addWriter(self, writer):
165         """
166         Add a FileDescriptor for notification of data available to write.
167         """
168         self._writes[writer] = 1
169
170     def removeReader(self, reader):
171         """
172         Remove a Selectable for notification of data available to read.
173         """
174         if reader in self._reads:
175             del self._reads[reader]
176
177     def removeWriter(self, writer):
178         """
179         Remove a Selectable for notification of data available to write.
180         """
181         if writer in self._writes:
182             del self._writes[writer]
183
184     def removeAll(self):
185         return self._removeAll(self._reads, self._writes)
186
187
188     def getReaders(self):
189         return self._reads.keys()
190
191
192     def getWriters(self):
193         return self._writes.keys()
194
195
196
197 def install():
198     """Configure the twisted mainloop to be run using the select() reactor.
199     """
200     reactor = SelectReactor()
201     from twisted.internet.main import installReactor
202     installReactor(reactor)
203
204 __all__ = ['install']
Note: See TracBrowser for help on using the browser.