132
|
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
|