Mercurial > projects > ldc
comparison tango/tango/io/selector/PollSelector.d @ 132:1700239cab2e trunk
[svn r136] MAJOR UNSTABLE UPDATE!!!
Initial commit after moving to Tango instead of Phobos.
Lots of bugfixes...
This build is not suitable for most things.
author | lindquist |
---|---|
date | Fri, 11 Jan 2008 17:57:40 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
131:5825d48b27d1 | 132:1700239cab2e |
---|---|
1 /******************************************************************************* | |
2 copyright: Copyright (c) 2006 Juan Jose Comellas. All rights reserved | |
3 license: BSD style: $(LICENSE) | |
4 author: Juan Jose Comellas <juanjo@comellas.com.ar> | |
5 *******************************************************************************/ | |
6 | |
7 module tango.io.selector.PollSelector; | |
8 | |
9 version (Posix) | |
10 { | |
11 public import tango.io.model.IConduit; | |
12 | |
13 private import tango.io.selector.model.ISelector; | |
14 private import tango.io.selector.AbstractSelector; | |
15 private import tango.io.selector.SelectorException; | |
16 private import tango.sys.Common; | |
17 private import tango.stdc.errno; | |
18 | |
19 version (linux) | |
20 private import tango.sys.linux.linux; | |
21 | |
22 debug (selector) | |
23 private import tango.io.Stdout; | |
24 | |
25 | |
26 /** | |
27 * Selector that uses the poll() system call to receive I/O events for | |
28 * the registered conduits. To use this class you would normally do | |
29 * something like this: | |
30 * | |
31 * Examples: | |
32 * --- | |
33 * import tango.io.selector.PollSelector; | |
34 * | |
35 * Socket socket; | |
36 * ISelector selector = new PollSelector(); | |
37 * | |
38 * selector.open(100, 10); | |
39 * | |
40 * // Register to read from socket | |
41 * selector.register(socket, Event.Read); | |
42 * | |
43 * int eventCount = selector.select(0.1); // 0.1 seconds | |
44 * if (eventCount > 0) | |
45 * { | |
46 * // We can now read from the socket | |
47 * socket.read(); | |
48 * } | |
49 * else if (eventCount == 0) | |
50 * { | |
51 * // Timeout | |
52 * } | |
53 * else if (eventCount == -1) | |
54 * { | |
55 * // Another thread called the wakeup() method. | |
56 * } | |
57 * else | |
58 * { | |
59 * // Error: should never happen. | |
60 * } | |
61 * | |
62 * selector.close(); | |
63 * --- | |
64 */ | |
65 public class PollSelector: AbstractSelector | |
66 { | |
67 /** | |
68 * Alias for the select() method as we're not reimplementing it in | |
69 * this class. | |
70 */ | |
71 alias AbstractSelector.select select; | |
72 | |
73 /** | |
74 * Default number of SelectionKey's that will be handled by the | |
75 * PollSelector. | |
76 */ | |
77 public const uint DefaultSize = 64; | |
78 | |
79 /** Map to associate the conduit handles with their selection keys */ | |
80 private PollSelectionKey[ISelectable.Handle] _keys; | |
81 private SelectionKey[] _selectedKeys; | |
82 private pollfd[] _pfds; | |
83 private uint _count = 0; | |
84 private int _eventCount = 0; | |
85 | |
86 /** | |
87 * Open the poll()-based selector. | |
88 * | |
89 * Params: | |
90 * size = maximum amount of conduits that will be registered; | |
91 * it will grow dynamically if needed. | |
92 * maxEvents = maximum amount of conduit events that will be | |
93 * returned in the selection set per call to select(); | |
94 * this value is currently not used by this selector. | |
95 */ | |
96 public void open(uint size = DefaultSize, uint maxEvents = DefaultSize) | |
97 in | |
98 { | |
99 assert(size > 0); | |
100 } | |
101 body | |
102 { | |
103 _pfds = new pollfd[size]; | |
104 } | |
105 | |
106 /** | |
107 * Close the selector. | |
108 * | |
109 * Remarks: | |
110 * It can be called multiple times without harmful side-effects. | |
111 */ | |
112 public void close() | |
113 { | |
114 _keys = null; | |
115 _selectedKeys = null; | |
116 _pfds = null; | |
117 _count = 0; | |
118 _eventCount = 0; | |
119 } | |
120 | |
121 /** | |
122 * Associate a conduit to the selector and track specific I/O events. | |
123 * | |
124 * Params: | |
125 * conduit = conduit that will be associated to the selector; | |
126 * must be a valid conduit (i.e. not null and open). | |
127 * events = bit mask of Event values that represent the events | |
128 * that will be tracked for the conduit. | |
129 * attachment = optional object with application-specific data that | |
130 * will be available when an event is triggered for the | |
131 * conduit | |
132 * | |
133 * Throws: | |
134 * RegisteredConduitException if the conduit had already been | |
135 * registered to the selector. | |
136 * | |
137 * Examples: | |
138 * --- | |
139 * selector.register(conduit, Event.Read | Event.Write, object); | |
140 * --- | |
141 */ | |
142 public void register(ISelectable conduit, Event events, Object attachment = null) | |
143 in | |
144 { | |
145 assert(conduit !is null && conduit.fileHandle() >= 0); | |
146 } | |
147 body | |
148 { | |
149 debug (selector) | |
150 Stdout.format("--- PollSelector.register(handle={0}, events=0x{1:x})\n", | |
151 cast(int) conduit.fileHandle(), cast(uint) events); | |
152 | |
153 // We make sure that the conduit is not already registered to | |
154 // the Selector | |
155 if ((conduit.fileHandle() in _keys) is null) | |
156 { | |
157 if (_count == _pfds.length) | |
158 _pfds.length = _pfds.length + 1; | |
159 | |
160 _pfds[_count].fd = conduit.fileHandle(); | |
161 _pfds[_count].events = cast(short) events; | |
162 _pfds[_count].revents = 0; | |
163 | |
164 _keys[conduit.fileHandle()] = new PollSelectionKey(conduit, events, _count, attachment); | |
165 _count++; | |
166 } | |
167 else | |
168 { | |
169 throw new RegisteredConduitException(__FILE__, __LINE__); | |
170 } | |
171 } | |
172 | |
173 /** | |
174 * Modify the events that are being tracked or the 'attachment' field | |
175 * for an already registered conduit. | |
176 * | |
177 * Params: | |
178 * conduit = conduit that will be associated to the selector; | |
179 * must be a valid conduit (i.e. not null and open). | |
180 * events = bit mask of Event values that represent the events | |
181 * that will be tracked for the conduit. | |
182 * attachment = optional object with application-specific data that | |
183 * will be available when an event is triggered for the | |
184 * conduit | |
185 * | |
186 * Remarks: | |
187 * The 'attachment' member of the SelectionKey will always be | |
188 * overwritten, even if it's null. | |
189 * | |
190 * Throws: | |
191 * UnregisteredConduitException if the conduit had not been previously | |
192 * registered to the selector. | |
193 * | |
194 * Examples: | |
195 * --- | |
196 * selector.reregister(conduit, Event.Write, object); | |
197 * --- | |
198 */ | |
199 public void reregister(ISelectable conduit, Event events, Object attachment = null) | |
200 in | |
201 { | |
202 assert(conduit !is null && conduit.fileHandle() >= 0); | |
203 } | |
204 body | |
205 { | |
206 debug (selector) | |
207 Stdout.format("--- PollSelector.reregister(handle={0}, events=0x{1:x})", | |
208 cast(int) conduit.fileHandle(), cast(uint) events); | |
209 | |
210 PollSelectionKey* current = (conduit.fileHandle() in _keys); | |
211 | |
212 if (current !is null) | |
213 { | |
214 debug (selector) | |
215 Stdout.format("--- Adding pollfd in index {0} (of {1})\n", | |
216 current.index, _count); | |
217 | |
218 (*current).events = events; | |
219 (*current).attachment = attachment; | |
220 | |
221 _pfds[current.index].events = cast(short) events; | |
222 } | |
223 else | |
224 { | |
225 throw new UnregisteredConduitException(__FILE__, __LINE__); | |
226 } | |
227 } | |
228 | |
229 /** | |
230 * Remove a conduit from the selector. | |
231 * | |
232 * Params: | |
233 * conduit = conduit that had been previously associated to the | |
234 * selector; it can be null. | |
235 * | |
236 * Remarks: | |
237 * Unregistering a null conduit is allowed and no exception is thrown | |
238 * if this happens. | |
239 * | |
240 * Throws: | |
241 * UnregisteredConduitException if the conduit had not been previously | |
242 * registered to the selector. | |
243 */ | |
244 public void unregister(ISelectable conduit) | |
245 { | |
246 if (conduit !is null) | |
247 { | |
248 try | |
249 { | |
250 debug (selector) | |
251 Stdout.format("--- PollSelector.unregister(handle={0})\n", | |
252 cast(int) conduit.fileHandle()); | |
253 | |
254 PollSelectionKey* removed = (conduit.fileHandle() in _keys); | |
255 | |
256 if (removed !is null) | |
257 { | |
258 debug (selector) | |
259 Stdout.format("--- Removing pollfd in index {0} (of {1})\n", | |
260 removed.index, _count); | |
261 | |
262 for (uint i = removed.index + 1; i > 0 && i < _count; i++) | |
263 { | |
264 _pfds[i - 1] = _pfds[i]; | |
265 } | |
266 _count--; | |
267 | |
268 _keys.remove(conduit.fileHandle()); | |
269 } | |
270 else | |
271 { | |
272 debug (selector) | |
273 Stdout.format("--- PollSelector.unregister(handle={0}): conduit was not found\n", | |
274 cast(int) conduit.fileHandle()); | |
275 throw new UnregisteredConduitException(__FILE__, __LINE__); | |
276 } | |
277 } | |
278 catch (Exception e) | |
279 { | |
280 debug (selector) | |
281 Stdout.format("--- Exception inside PollSelector.unregister(handle={0}): {1}", | |
282 cast(int) conduit.fileHandle(), e.toString()); | |
283 | |
284 throw new UnregisteredConduitException(__FILE__, __LINE__); | |
285 } | |
286 } | |
287 } | |
288 | |
289 /** | |
290 * Wait for I/O events from the registered conduits for a specified | |
291 * amount of time. | |
292 * | |
293 * Params: | |
294 * timeout = Timespan with the maximum amount of time that the | |
295 * selector will wait for events from the conduits; the | |
296 * amount of time is relative to the current system time | |
297 * (i.e. just the number of milliseconds that the selector | |
298 * has to wait for the events). | |
299 * | |
300 * Returns: | |
301 * The amount of conduits that have received events; 0 if no conduits | |
302 * have received events within the specified timeout; and -1 if the | |
303 * wakeup() method has been called from another thread. | |
304 * | |
305 * Throws: | |
306 * InterruptedSystemCallException if the underlying system call was | |
307 * interrupted by a signal and the 'restartInterruptedSystemCall' | |
308 * property was set to false; SelectorException if there were no | |
309 * resources available to wait for events from the conduits. | |
310 */ | |
311 public int select(TimeSpan timeout) | |
312 { | |
313 int to = (timeout != TimeSpan.max ? cast(int) timeout.millis : -1); | |
314 | |
315 debug (selector) | |
316 Stdout.format("--- PollSelector.select({0} ms): waiting on {1} handles\n", | |
317 to, _count); | |
318 | |
319 // We run the call to poll() inside a loop in case the system call | |
320 // was interrupted by a signal and we need to restart it. | |
321 while (true) | |
322 { | |
323 _eventCount = poll(_pfds.ptr, _count, to); | |
324 if (_eventCount > 0) | |
325 { | |
326 int i = 0; | |
327 PollSelectionKey* key; | |
328 | |
329 if (_selectedKeys is null) | |
330 { | |
331 _selectedKeys = new SelectionKey[16]; | |
332 } | |
333 | |
334 // FIXME: add support for the wakeup() call. | |
335 foreach (pollfd pfd; _pfds[0 .. _count]) | |
336 { | |
337 if (i < _eventCount) | |
338 { | |
339 if (pfd.revents != 0) | |
340 { | |
341 debug (selector) | |
342 Stdout.format("--- Found events 0x{0:x} for handle {1} (index {2})\n", | |
343 cast(uint) pfd.revents, cast(int) pfd.fd, i); | |
344 | |
345 // Find the key whose handle received an event | |
346 key = ((cast(ISelectable.Handle) pfd.fd) in _keys); | |
347 if (key !is null) | |
348 { | |
349 // Enlarge the array of necessary | |
350 if (i >= _selectedKeys.length) | |
351 { | |
352 // The underlying array worries about | |
353 // incrementing the allocated block | |
354 // efficiently. | |
355 _selectedKeys.length = i + 1; | |
356 } | |
357 | |
358 (*key).events = cast(Event) pfd.revents; | |
359 | |
360 _selectedKeys[i] = *key; | |
361 i++; | |
362 } | |
363 else | |
364 { | |
365 debug (selector) | |
366 Stdout.format("--- Handle {0} was not found in the Selector\n", | |
367 cast(int) pfd.fd); | |
368 } | |
369 } | |
370 } | |
371 else | |
372 { | |
373 break; | |
374 } | |
375 } | |
376 _selectedKeys.length = i; | |
377 break; | |
378 } | |
379 else if (_eventCount == 0) | |
380 { | |
381 // Timeout | |
382 break; | |
383 } | |
384 else // if (eventCount < 0) | |
385 { | |
386 if (errno != EINTR || !_restartInterruptedSystemCall) | |
387 { | |
388 // The call to checkErrno() ends up throwing an exception | |
389 checkErrno(__FILE__, __LINE__); | |
390 } | |
391 debug (selector) | |
392 Stdout.print("--- Restarting poll() after being interrupted\n"); | |
393 } | |
394 } | |
395 return _eventCount; | |
396 } | |
397 | |
398 /** | |
399 * Return the selection set resulting from the call to any of the | |
400 * select() methods. | |
401 * | |
402 * Remarks: | |
403 * If the call to select() was unsuccessful or it did not return any | |
404 * events, the returned value will be null. | |
405 */ | |
406 public ISelectionSet selectedSet() | |
407 { | |
408 return (_eventCount > 0 ? new PollSelectionSet(_selectedKeys) : null); | |
409 } | |
410 | |
411 /** | |
412 * Return the selection key resulting from the registration of a | |
413 * conduit to the selector. | |
414 * | |
415 * Remarks: | |
416 * If the conduit is not registered to the selector the returned | |
417 * value will be null. No exception will be thrown by this method. | |
418 */ | |
419 public SelectionKey key(ISelectable conduit) | |
420 { | |
421 return (conduit !is null ? _keys[conduit.fileHandle()] : null); | |
422 } | |
423 | |
424 unittest | |
425 { | |
426 } | |
427 } | |
428 | |
429 /** | |
430 * Class used to hold the list of Conduits that have received events. | |
431 */ | |
432 private class PollSelectionSet: ISelectionSet | |
433 { | |
434 private SelectionKey[] _keys; | |
435 | |
436 protected this(SelectionKey[] keys) | |
437 { | |
438 _keys = keys; | |
439 } | |
440 | |
441 public uint length() | |
442 { | |
443 return _keys.length; | |
444 } | |
445 | |
446 /** | |
447 * Iterate over all the Conduits that have received events. | |
448 */ | |
449 public int opApply(int delegate(inout SelectionKey) dg) | |
450 { | |
451 int rc = 0; | |
452 | |
453 foreach (SelectionKey current; _keys) | |
454 { | |
455 if (dg(current) != 0) | |
456 { | |
457 rc = -1; | |
458 break; | |
459 } | |
460 } | |
461 return rc; | |
462 } | |
463 } | |
464 | |
465 /** | |
466 * Class that holds the information that the PollSelector needs to deal | |
467 * with each registered Conduit. | |
468 */ | |
469 private class PollSelectionKey: SelectionKey | |
470 { | |
471 private uint _index; | |
472 | |
473 public this() | |
474 { | |
475 } | |
476 | |
477 public this(ISelectable conduit, Event events, uint index, Object attachment) | |
478 { | |
479 super(conduit, events, attachment); | |
480 | |
481 _index = index; | |
482 } | |
483 | |
484 public uint index() | |
485 { | |
486 return _index; | |
487 } | |
488 | |
489 public void index(uint index) | |
490 { | |
491 _index = index; | |
492 } | |
493 } | |
494 } | |
495 |