OLD | NEW |
| (Empty) |
1 # Copyright (c) 2001-2007 Twisted Matrix Laboratories. | |
2 # See LICENSE for details. | |
3 | |
4 | |
5 """ | |
6 A poll() based implementation of the twisted main loop. | |
7 | |
8 To install the event loop (and you should do this before any connections, | |
9 listeners or connectors are added):: | |
10 | |
11 from twisted.internet import pollreactor | |
12 pollreactor.install() | |
13 | |
14 Maintainer: U{Itamar Shtull-Trauring<mailto:twisted@itamarst.org>} | |
15 """ | |
16 | |
17 # System imports | |
18 import errno, sys | |
19 from select import error as SelectError, poll | |
20 from select import POLLIN, POLLOUT, POLLHUP, POLLERR, POLLNVAL | |
21 | |
22 from zope.interface import implements | |
23 | |
24 # Twisted imports | |
25 from twisted.python import log | |
26 from twisted.internet import main, posixbase, error | |
27 from twisted.internet.interfaces import IReactorFDSet | |
28 | |
29 POLL_DISCONNECTED = (POLLHUP | POLLERR | POLLNVAL) | |
30 | |
31 | |
32 class PollReactor(posixbase.PosixReactorBase): | |
33 """ | |
34 A reactor that uses poll(2). | |
35 | |
36 @ivar _poller: A L{poll} which will be used to check for I/O | |
37 readiness. | |
38 | |
39 @ivar _selectables: A dictionary mapping integer file descriptors to | |
40 instances of L{FileDescriptor} which have been registered with the | |
41 reactor. All L{FileDescriptors} which are currently receiving read or | |
42 write readiness notifications will be present as values in this | |
43 dictionary. | |
44 | |
45 @ivar _reads: A dictionary mapping integer file descriptors to arbitrary | |
46 values (this is essentially a set). Keys in this dictionary will be | |
47 registered with C{_poller} for read readiness notifications which will | |
48 be dispatched to the corresponding L{FileDescriptor} instances in | |
49 C{_selectables}. | |
50 | |
51 @ivar _writes: A dictionary mapping integer file descriptors to arbitrary | |
52 values (this is essentially a set). Keys in this dictionary will be | |
53 registered with C{_poller} for write readiness notifications which will | |
54 be dispatched to the corresponding L{FileDescriptor} instances in | |
55 C{_selectables}. | |
56 """ | |
57 implements(IReactorFDSet) | |
58 | |
59 def __init__(self): | |
60 """ | |
61 Initialize polling object, file descriptor tracking dictionaries, and | |
62 the base class. | |
63 """ | |
64 self._poller = poll() | |
65 self._selectables = {} | |
66 self._reads = {} | |
67 self._writes = {} | |
68 posixbase.PosixReactorBase.__init__(self) | |
69 | |
70 | |
71 def _updateRegistration(self, fd): | |
72 """Register/unregister an fd with the poller.""" | |
73 try: | |
74 self._poller.unregister(fd) | |
75 except KeyError: | |
76 pass | |
77 | |
78 mask = 0 | |
79 if fd in self._reads: | |
80 mask = mask | POLLIN | |
81 if fd in self._writes: | |
82 mask = mask | POLLOUT | |
83 if mask != 0: | |
84 self._poller.register(fd, mask) | |
85 else: | |
86 if fd in self._selectables: | |
87 del self._selectables[fd] | |
88 | |
89 def _dictRemove(self, selectable, mdict): | |
90 try: | |
91 # the easy way | |
92 fd = selectable.fileno() | |
93 # make sure the fd is actually real. In some situations we can get | |
94 # -1 here. | |
95 mdict[fd] | |
96 except: | |
97 # the hard way: necessary because fileno() may disappear at any | |
98 # moment, thanks to python's underlying sockets impl | |
99 for fd, fdes in self._selectables.items(): | |
100 if selectable is fdes: | |
101 break | |
102 else: | |
103 # Hmm, maybe not the right course of action? This method can't | |
104 # fail, because it happens inside error detection... | |
105 return | |
106 if fd in mdict: | |
107 del mdict[fd] | |
108 self._updateRegistration(fd) | |
109 | |
110 def addReader(self, reader): | |
111 """Add a FileDescriptor for notification of data available to read. | |
112 """ | |
113 fd = reader.fileno() | |
114 if fd not in self._reads: | |
115 self._selectables[fd] = reader | |
116 self._reads[fd] = 1 | |
117 self._updateRegistration(fd) | |
118 | |
119 def addWriter(self, writer): | |
120 """Add a FileDescriptor for notification of data available to write. | |
121 """ | |
122 fd = writer.fileno() | |
123 if fd not in self._writes: | |
124 self._selectables[fd] = writer | |
125 self._writes[fd] = 1 | |
126 self._updateRegistration(fd) | |
127 | |
128 def removeReader(self, reader): | |
129 """Remove a Selectable for notification of data available to read. | |
130 """ | |
131 return self._dictRemove(reader, self._reads) | |
132 | |
133 def removeWriter(self, writer): | |
134 """Remove a Selectable for notification of data available to write. | |
135 """ | |
136 return self._dictRemove(writer, self._writes) | |
137 | |
138 def removeAll(self): | |
139 """Remove all selectables, and return a list of them.""" | |
140 if self.waker is not None: | |
141 self.removeReader(self.waker) | |
142 result = self._selectables.values() | |
143 fds = self._selectables.keys() | |
144 self._reads.clear() | |
145 self._writes.clear() | |
146 self._selectables.clear() | |
147 for fd in fds: | |
148 self._poller.unregister(fd) | |
149 | |
150 if self.waker is not None: | |
151 self.addReader(self.waker) | |
152 return result | |
153 | |
154 def doPoll(self, timeout): | |
155 """Poll the poller for new events.""" | |
156 if timeout is not None: | |
157 timeout = int(timeout * 1000) # convert seconds to milliseconds | |
158 | |
159 try: | |
160 l = self._poller.poll(timeout) | |
161 except SelectError, e: | |
162 if e[0] == errno.EINTR: | |
163 return | |
164 else: | |
165 raise | |
166 _drdw = self._doReadOrWrite | |
167 for fd, event in l: | |
168 try: | |
169 selectable = self._selectables[fd] | |
170 except KeyError: | |
171 # Handles the infrequent case where one selectable's | |
172 # handler disconnects another. | |
173 continue | |
174 log.callWithLogger(selectable, _drdw, selectable, fd, event) | |
175 | |
176 doIteration = doPoll | |
177 | |
178 def _doReadOrWrite(self, selectable, fd, event): | |
179 why = None | |
180 inRead = False | |
181 if event & POLL_DISCONNECTED and not (event & POLLIN): | |
182 why = main.CONNECTION_LOST | |
183 else: | |
184 try: | |
185 if event & POLLIN: | |
186 why = selectable.doRead() | |
187 inRead = True | |
188 if not why and event & POLLOUT: | |
189 why = selectable.doWrite() | |
190 inRead = False | |
191 if not selectable.fileno() == fd: | |
192 why = error.ConnectionFdescWentAway('Filedescriptor went awa
y') | |
193 inRead = False | |
194 except: | |
195 log.deferr() | |
196 why = sys.exc_info()[1] | |
197 if why: | |
198 self._disconnectSelectable(selectable, why, inRead) | |
199 | |
200 | |
201 def getReaders(self): | |
202 return [self._selectables[fd] for fd in self._reads] | |
203 | |
204 | |
205 def getWriters(self): | |
206 return [self._selectables[fd] for fd in self._writes] | |
207 | |
208 | |
209 | |
210 def install(): | |
211 """Install the poll() reactor.""" | |
212 p = PollReactor() | |
213 from twisted.internet.main import installReactor | |
214 installReactor(p) | |
215 | |
216 | |
217 __all__ = ["PollReactor", "install"] | |
OLD | NEW |