1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 """
36 A poll() based ClusterShell Engine.
37
38 The poll() system call is available on Linux and BSD.
39 """
40
41 import errno
42 import select
43 import sys
44 import time
45
46 from ClusterShell.Engine.Engine import Engine
47 from ClusterShell.Engine.Engine import EngineException
48 from ClusterShell.Engine.Engine import EngineNotSupportedError
49 from ClusterShell.Engine.Engine import EngineTimeoutException
50 from ClusterShell.Worker.EngineClient import EngineClientEOF
51
52
54 """
55 Poll Engine
56
57 ClusterShell engine using the select.poll mechanism (Linux poll()
58 syscall).
59 """
60
61 identifier = "poll"
62
73
81
85
87 """
88 Engine-specific modifications after a interesting event change for
89 a file descriptor. Called automatically by Engine register/unregister and
90 set_events(). For the poll() engine, it reg/unreg or modifies the event mask
91 associated to a file descriptor.
92 """
93 self._debug("MODSPEC fd=%d event=%x setvalue=%d" % (fd, event,
94 setvalue))
95 if setvalue:
96 eventmask = 0
97 if event & (Engine.E_READ | Engine.E_ERROR):
98 eventmask = select.POLLIN
99 elif event == Engine.E_WRITE:
100 eventmask = select.POLLOUT
101 self.polling.register(fd, eventmask)
102 else:
103 self.polling.unregister(fd)
104
106 """
107 Poll engine run(): start clients and properly get replies
108 """
109 if timeout == 0:
110 timeout = -1
111
112 start_time = time.time()
113
114
115 while self.evlooprefcnt > 0:
116 self._debug("LOOP evlooprefcnt=%d (reg_clifds=%s) (timers=%d)" \
117 % (self.evlooprefcnt, self.reg_clifds.keys(), \
118 len(self.timerq)))
119 try:
120 timeo = self.timerq.nextfire_delay()
121 if timeout > 0 and timeo >= timeout:
122
123 self.timerq.clear()
124 timeo = timeout
125 elif timeo == -1:
126 timeo = timeout
127
128 self.reg_clifds_changed = False
129 evlist = self.polling.poll(timeo * 1000.0 + 1.0)
130
131 except select.error, (ex_errno, ex_strerror):
132
133 if ex_errno == errno.EINTR:
134 continue
135 elif ex_errno == errno.EINVAL:
136 print >> sys.stderr, \
137 "EnginePoll: please increase RLIMIT_NOFILE"
138 raise
139
140 for fd, event in evlist:
141
142 if event & select.POLLNVAL:
143 raise EngineException("Caught POLLNVAL on fd %d" % fd)
144
145 if self.reg_clifds_changed:
146 self._debug("REG CLIENTS CHANGED - Aborting current evlist")
147
148 break
149
150
151 client, fdev = self._fd2client(fd)
152 if not client or fdev is None:
153 continue
154
155
156 client._processing = True
157
158
159 if event & select.POLLERR:
160 self._debug("POLLERR %s" % client)
161 self.unregister_writer(client)
162 client.file_writer.close()
163 client.file_writer = None
164 continue
165
166
167 if event & select.POLLIN:
168 assert fdev & (Engine.E_READ | Engine.E_ERROR)
169 assert client._events & fdev
170 self.modify(client, 0, fdev)
171 try:
172 if fdev & Engine.E_READ:
173 client._handle_read()
174 else:
175 client._handle_error()
176 except EngineClientEOF, e:
177 self._debug("EngineClientEOF %s" % client)
178 if fdev & Engine.E_READ:
179 self.remove(client)
180 continue
181
182
183
184 elif event & select.POLLHUP:
185 self._debug("POLLHUP fd=%d %s (r%s,e%s,w%s)" % (fd,
186 client.__class__.__name__, client.reader_fileno(),
187 client.error_fileno(), client.writer_fileno()))
188 client._processing = False
189
190 if fdev & Engine.E_READ:
191 if client._events & Engine.E_ERROR:
192 self.modify(client, 0, fdev)
193 else:
194 self.remove(client)
195 else:
196 if client._events & Engine.E_READ:
197 self.modify(client, 0, fdev)
198 else:
199 self.remove(client)
200 continue
201
202
203 if event & select.POLLOUT:
204 self._debug("POLLOUT fd=%d %s (r%s,e%s,w%s)" % (fd,
205 client.__class__.__name__, client.reader_fileno(),
206 client.error_fileno(), client.writer_fileno()))
207 assert fdev == Engine.E_WRITE
208 assert client._events & fdev
209 self.modify(client, 0, fdev)
210 client._handle_write()
211
212
213 client._processing = False
214
215
216 if client.registered:
217 self.set_events(client, client._new_events)
218
219
220 if timeout > 0 and time.time() >= start_time + timeout:
221 raise EngineTimeoutException()
222
223
224 self.fire_timers()
225
226 self._debug("LOOP EXIT evlooprefcnt=%d (reg_clifds=%s) (timers=%d)" % \
227 (self.evlooprefcnt, self.reg_clifds, len(self.timerq)))
228