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