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.SelectSelector;
|
|
8
|
|
9 public import tango.io.model.IConduit;
|
|
10
|
|
11 private import tango.io.selector.model.ISelector;
|
|
12 private import tango.io.selector.AbstractSelector;
|
|
13 private import tango.io.selector.SelectorException;
|
|
14 private import tango.sys.Common;
|
|
15
|
|
16 private import tango.stdc.errno;
|
|
17
|
|
18 debug (selector)
|
|
19 {
|
|
20 private import tango.io.Stdout;
|
|
21 private import tango.text.convert.Integer;
|
|
22 }
|
|
23
|
|
24
|
|
25 version (Windows)
|
|
26 {
|
|
27 import tango.core.Thread;
|
|
28
|
|
29 private
|
|
30 {
|
|
31 // Opaque struct
|
|
32 struct fd_set
|
|
33 {
|
|
34 }
|
|
35
|
|
36 extern (Windows) int select(int nfds, fd_set* readfds, fd_set* writefds,
|
|
37 fd_set* errorfds, timeval* timeout);
|
|
38 }
|
|
39 }
|
|
40
|
|
41
|
|
42 /**
|
|
43 * Selector that uses the select() system call to receive I/O events for
|
|
44 * the registered conduits. To use this class you would normally do
|
|
45 * something like this:
|
|
46 *
|
|
47 * Examples:
|
|
48 * ---
|
|
49 * import tango.io.selector.SelectSelector;
|
|
50 *
|
|
51 * Socket socket;
|
|
52 * ISelector selector = new SelectSelector();
|
|
53 *
|
|
54 * selector.open(100, 10);
|
|
55 *
|
|
56 * // Register to read from socket
|
|
57 * selector.register(socket, Event.Read);
|
|
58 *
|
|
59 * int eventCount = selector.select(0.1); // 0.1 seconds
|
|
60 * if (eventCount > 0)
|
|
61 * {
|
|
62 * // We can now read from the socket
|
|
63 * socket.read();
|
|
64 * }
|
|
65 * else if (eventCount == 0)
|
|
66 * {
|
|
67 * // Timeout
|
|
68 * }
|
|
69 * else if (eventCount == -1)
|
|
70 * {
|
|
71 * // Another thread called the wakeup() method.
|
|
72 * }
|
|
73 * else
|
|
74 * {
|
|
75 * // Error: should never happen.
|
|
76 * }
|
|
77 *
|
|
78 * selector.close();
|
|
79 * ---
|
|
80 */
|
|
81 public class SelectSelector: AbstractSelector
|
|
82 {
|
|
83 /**
|
|
84 * Alias for the select() method as we're not reimplementing it in
|
|
85 * this class.
|
|
86 */
|
|
87 alias AbstractSelector.select select;
|
|
88
|
|
89 uint _size;
|
|
90 private SelectionKey[ISelectable.Handle] _keys;
|
|
91 private HandleSet _readSet;
|
|
92 private HandleSet _writeSet;
|
|
93 private HandleSet _exceptionSet;
|
|
94 private HandleSet _selectedReadSet;
|
|
95 private HandleSet _selectedWriteSet;
|
|
96 private HandleSet _selectedExceptionSet;
|
|
97 int _eventCount;
|
|
98 version (Posix)
|
|
99 {
|
|
100 private ISelectable.Handle _maxfd = cast(ISelectable.Handle) -1;
|
|
101
|
|
102 /**
|
|
103 * Default number of SelectionKey's that will be handled by the
|
|
104 * SelectSelector.
|
|
105 */
|
|
106 public const uint DefaultSize = 1024;
|
|
107 }
|
|
108 else
|
|
109 {
|
|
110 /**
|
|
111 * Default number of SelectionKey's that will be handled by the
|
|
112 * SelectSelector.
|
|
113 */
|
|
114 public const uint DefaultSize = 63;
|
|
115 }
|
|
116
|
|
117 /**
|
|
118 * Open the select()-based selector.
|
|
119 *
|
|
120 * Params:
|
|
121 * size = maximum amount of conduits that will be registered;
|
|
122 * it will grow dynamically if needed.
|
|
123 * maxEvents = maximum amount of conduit events that will be
|
|
124 * returned in the selection set per call to select();
|
|
125 * this value is currently not used by this selector.
|
|
126 */
|
|
127 public void open(uint size = DefaultSize, uint maxEvents = DefaultSize)
|
|
128 in
|
|
129 {
|
|
130 assert(size > 0);
|
|
131 }
|
|
132 body
|
|
133 {
|
|
134 _size = size;
|
|
135 }
|
|
136
|
|
137 /**
|
|
138 * Close the selector.
|
|
139 *
|
|
140 * Remarks:
|
|
141 * It can be called multiple times without harmful side-effects.
|
|
142 */
|
|
143 public void close()
|
|
144 {
|
|
145 _size = 0;
|
|
146 _keys = null;
|
|
147 _readSet = null;
|
|
148 _writeSet = null;
|
|
149 _exceptionSet = null;
|
|
150 _selectedReadSet = null;
|
|
151 _selectedWriteSet = null;
|
|
152 _selectedExceptionSet = null;
|
|
153 }
|
|
154
|
|
155 /**
|
|
156 * Associate a conduit to the selector and track specific I/O events.
|
|
157 *
|
|
158 * Params:
|
|
159 * conduit = conduit that will be associated to the selector;
|
|
160 * must be a valid conduit (i.e. not null and open).
|
|
161 * events = bit mask of Event values that represent the events
|
|
162 * that will be tracked for the conduit.
|
|
163 * attachment = optional object with application-specific data that
|
|
164 * will be available when an event is triggered for the
|
|
165 * conduit
|
|
166 *
|
|
167 * Throws:
|
|
168 * RegisteredConduitException if the conduit had already been
|
|
169 * registered to the selector.
|
|
170 *
|
|
171 * Examples:
|
|
172 * ---
|
|
173 * selector.register(conduit, Event.Read | Event.Write, object);
|
|
174 * ---
|
|
175 */
|
|
176 public void register(ISelectable conduit, Event events, Object attachment = null)
|
|
177 in
|
|
178 {
|
|
179 assert(conduit !is null && conduit.fileHandle() >= 0);
|
|
180 }
|
|
181 body
|
|
182 {
|
|
183 ISelectable.Handle handle = conduit.fileHandle();
|
|
184
|
|
185 debug (selector)
|
|
186 Stdout.format("--- SelectSelector.register(handle={0}, events=0x{1:x})\n",
|
|
187 cast(int) handle, cast(uint) events);
|
|
188
|
|
189 // We make sure that the conduit is not already registered to
|
|
190 // the Selector
|
|
191 SelectionKey* key = (conduit.fileHandle() in _keys);
|
|
192
|
|
193 if (key is null)
|
|
194 {
|
|
195 // Keep record of the Conduits for whom we're tracking events.
|
|
196 _keys[handle] = new SelectionKey(conduit, events, attachment);
|
|
197
|
|
198 if ((events & Event.Read) || (events & Event.Hangup))
|
|
199 {
|
|
200 if (_readSet is null)
|
|
201 {
|
|
202 _readSet = new HandleSet(_size);
|
|
203 _selectedReadSet = new HandleSet(_size);
|
|
204 }
|
|
205 _readSet.set(handle);
|
|
206 }
|
|
207
|
|
208 if (events & Event.Write)
|
|
209 {
|
|
210 if (_writeSet is null)
|
|
211 {
|
|
212 _writeSet = new HandleSet(_size);
|
|
213 _selectedWriteSet = new HandleSet(_size);
|
|
214 }
|
|
215 _writeSet.set(handle);
|
|
216 }
|
|
217
|
|
218 if (events & Event.Error)
|
|
219 {
|
|
220 if (_exceptionSet is null)
|
|
221 {
|
|
222 _exceptionSet = new HandleSet(_size);
|
|
223 _selectedExceptionSet = new HandleSet(_size);
|
|
224 }
|
|
225 _exceptionSet.set(handle);
|
|
226 }
|
|
227
|
|
228 version (Posix)
|
|
229 {
|
|
230 if (handle > _maxfd)
|
|
231 _maxfd = handle;
|
|
232 }
|
|
233 }
|
|
234 else
|
|
235 {
|
|
236 throw new RegisteredConduitException(__FILE__, __LINE__);
|
|
237 }
|
|
238 }
|
|
239
|
|
240 /**
|
|
241 * Modify the events that are being tracked or the 'attachment' field
|
|
242 * for an already registered conduit.
|
|
243 *
|
|
244 * Params:
|
|
245 * conduit = conduit that will be associated to the selector;
|
|
246 * must be a valid conduit (i.e. not null and open).
|
|
247 * events = bit mask of Event values that represent the events
|
|
248 * that will be tracked for the conduit.
|
|
249 * attachment = optional object with application-specific data that
|
|
250 * will be available when an event is triggered for the
|
|
251 * conduit
|
|
252 *
|
|
253 * Remarks:
|
|
254 * The 'attachment' member of the SelectionKey will always be
|
|
255 * overwritten, even if it's null.
|
|
256 *
|
|
257 * Throws:
|
|
258 * UnregisteredConduitException if the conduit had not been previously
|
|
259 * registered to the selector.
|
|
260 *
|
|
261 * Examples:
|
|
262 * ---
|
|
263 * selector.reregister(conduit, Event.Write, object);
|
|
264 * ---
|
|
265 */
|
|
266 public void reregister(ISelectable conduit, Event events, Object attachment = null)
|
|
267 in
|
|
268 {
|
|
269 assert(conduit !is null && conduit.fileHandle() >= 0);
|
|
270 }
|
|
271 body
|
|
272 {
|
|
273 ISelectable.Handle handle = conduit.fileHandle();
|
|
274
|
|
275 debug (selector)
|
|
276 Stdout.format("--- SelectSelector.reregister(handle={0}, events=0x{1:x})\n",
|
|
277 cast(int) handle, cast(uint) events);
|
|
278
|
|
279 SelectionKey *key = (handle in _keys);
|
|
280 if (key !is null)
|
|
281 {
|
|
282 if ((events & Event.Read) || (events & Event.Hangup))
|
|
283 {
|
|
284 if (_readSet is null)
|
|
285 {
|
|
286 _readSet = new HandleSet(_size);
|
|
287 _selectedReadSet = new HandleSet(_size);
|
|
288 }
|
|
289 _readSet.set(handle);
|
|
290 }
|
|
291 else if (_readSet !is null)
|
|
292 {
|
|
293 _readSet.clear(handle);
|
|
294 }
|
|
295
|
|
296 if ((events & Event.Write))
|
|
297 {
|
|
298 if (_writeSet is null)
|
|
299 {
|
|
300 _writeSet = new HandleSet(_size);
|
|
301 _selectedWriteSet = new HandleSet(_size);
|
|
302 }
|
|
303 _writeSet.set(handle);
|
|
304 }
|
|
305 else if (_writeSet !is null)
|
|
306 {
|
|
307 _writeSet.clear(handle);
|
|
308 }
|
|
309
|
|
310 if (events & Event.Error)
|
|
311 {
|
|
312 if (_exceptionSet is null)
|
|
313 {
|
|
314 _exceptionSet = new HandleSet(_size);
|
|
315 _selectedExceptionSet = new HandleSet(_size);
|
|
316 }
|
|
317 _exceptionSet.set(handle);
|
|
318 }
|
|
319 else if (_exceptionSet !is null)
|
|
320 {
|
|
321 _exceptionSet.clear(handle);
|
|
322 }
|
|
323
|
|
324 version (Posix)
|
|
325 {
|
|
326 if (handle > _maxfd)
|
|
327 _maxfd = handle;
|
|
328 }
|
|
329
|
|
330 (*key).events = events;
|
|
331 (*key).attachment = attachment;
|
|
332 }
|
|
333 else
|
|
334 {
|
|
335 throw new UnregisteredConduitException(__FILE__, __LINE__);
|
|
336 }
|
|
337 }
|
|
338
|
|
339 /**
|
|
340 * Remove a conduit from the selector.
|
|
341 *
|
|
342 * Params:
|
|
343 * conduit = conduit that had been previously associated to the
|
|
344 * selector; it can be null.
|
|
345 *
|
|
346 * Remarks:
|
|
347 * Unregistering a null conduit is allowed and no exception is thrown
|
|
348 * if this happens.
|
|
349 *
|
|
350 * Throws:
|
|
351 * UnregisteredConduitException if the conduit had not been previously
|
|
352 * registered to the selector.
|
|
353 */
|
|
354 public void unregister(ISelectable conduit)
|
|
355 {
|
|
356 if (conduit !is null)
|
|
357 {
|
|
358 ISelectable.Handle handle = conduit.fileHandle();
|
|
359
|
|
360 debug (selector)
|
|
361 Stdout.format("--- SelectSelector.unregister(handle={0})\n",
|
|
362 cast(int) handle);
|
|
363
|
|
364 SelectionKey* removed = (handle in _keys);
|
|
365
|
|
366 if (removed !is null)
|
|
367 {
|
|
368 if (_exceptionSet !is null)
|
|
369 {
|
|
370 _exceptionSet.clear(handle);
|
|
371 }
|
|
372 if (_writeSet !is null)
|
|
373 {
|
|
374 _writeSet.clear(handle);
|
|
375 }
|
|
376 if (_readSet !is null)
|
|
377 {
|
|
378 _readSet.clear(handle);
|
|
379 }
|
|
380 _keys.remove(handle);
|
|
381
|
|
382 version (Posix)
|
|
383 {
|
|
384 // If we're removing the biggest handle we've entered so far
|
|
385 // we need to recalculate this value for the set.
|
|
386 if (handle == _maxfd)
|
|
387 {
|
|
388 while (--_maxfd >= 0)
|
|
389 {
|
|
390 if ((_readSet !is null && _readSet.isSet(_maxfd)) ||
|
|
391 (_writeSet !is null && _writeSet.isSet(_maxfd)) ||
|
|
392 (_exceptionSet !is null && _exceptionSet.isSet(_maxfd)))
|
|
393 {
|
|
394 break;
|
|
395 }
|
|
396 }
|
|
397 }
|
|
398 }
|
|
399 }
|
|
400 else
|
|
401 {
|
|
402 debug (selector)
|
|
403 Stdout.format("--- SelectSelector.unregister(handle={0}): conduit was not found\n",
|
|
404 cast(int) conduit.fileHandle());
|
|
405 throw new UnregisteredConduitException(__FILE__, __LINE__);
|
|
406 }
|
|
407 }
|
|
408 }
|
|
409
|
|
410 /**
|
|
411 * Wait for I/O events from the registered conduits for a specified
|
|
412 * amount of time.
|
|
413 *
|
|
414 * Params:
|
|
415 * timeout = TimeSpan with the maximum amount of time that the
|
|
416 * selector will wait for events from the conduits; the
|
|
417 * amount of time is relative to the current system time
|
|
418 * (i.e. just the number of milliseconds that the selector
|
|
419 * has to wait for the events).
|
|
420 *
|
|
421 * Returns:
|
|
422 * The amount of conduits that have received events; 0 if no conduits
|
|
423 * have received events within the specified timeout; and -1 if the
|
|
424 * wakeup() method has been called from another thread.
|
|
425 *
|
|
426 * Throws:
|
|
427 * InterruptedSystemCallException if the underlying system call was
|
|
428 * interrupted by a signal and the 'restartInterruptedSystemCall'
|
|
429 * property was set to false; SelectorException if there were no
|
|
430 * resources available to wait for events from the conduits.
|
|
431 */
|
|
432 public int select(TimeSpan timeout)
|
|
433 {
|
|
434 fd_set *readfds;
|
|
435 fd_set *writefds;
|
|
436 fd_set *exceptfds;
|
|
437 timeval tv;
|
|
438 version (Windows)
|
|
439 bool handlesAvailable = false;
|
|
440
|
|
441 debug (selector)
|
|
442 Stdout.format("--- SelectSelector.select(timeout={0} msec)\n", timeout.millis);
|
|
443
|
|
444 if (_readSet !is null)
|
|
445 {
|
|
446 debug (selector)
|
|
447 _readSet.dump("_readSet");
|
|
448
|
|
449 version (Windows)
|
|
450 handlesAvailable = handlesAvailable || (_readSet.length > 0);
|
|
451
|
|
452 readfds = cast(fd_set*) _selectedReadSet.copy(_readSet);
|
|
453 }
|
|
454 if (_writeSet !is null)
|
|
455 {
|
|
456 debug (selector)
|
|
457 _writeSet.dump("_writeSet");
|
|
458
|
|
459 version (Windows)
|
|
460 handlesAvailable = handlesAvailable || (_writeSet.length > 0);
|
|
461
|
|
462 writefds = cast(fd_set*) _selectedWriteSet.copy(_writeSet);
|
|
463 }
|
|
464 if (_exceptionSet !is null)
|
|
465 {
|
|
466 debug (selector)
|
|
467 _exceptionSet.dump("_exceptionSet");
|
|
468
|
|
469 version (Windows)
|
|
470 handlesAvailable = handlesAvailable || (_exceptionSet.length > 0);
|
|
471
|
|
472 exceptfds = cast(fd_set*) _selectedExceptionSet.copy(_exceptionSet);
|
|
473 }
|
|
474
|
|
475 version (Posix)
|
|
476 {
|
|
477 while (true)
|
|
478 {
|
|
479 toTimeval(&tv, timeout);
|
|
480
|
|
481 // FIXME: add support for the wakeup() call.
|
|
482 _eventCount = .select(_maxfd + 1, readfds, writefds, exceptfds, timeout is TimeSpan.max ? null : &tv);
|
|
483
|
|
484 debug (selector)
|
|
485 Stdout.format("--- .select() returned {0} (maxfd={1})\n",
|
|
486 _eventCount, cast(int) _maxfd);
|
|
487 if (_eventCount >= 0)
|
|
488 {
|
|
489 break;
|
|
490 }
|
|
491 else
|
|
492 {
|
|
493 if (errno != EINTR || !_restartInterruptedSystemCall)
|
|
494 {
|
|
495 // checkErrno() always throws an exception
|
|
496 checkErrno(__FILE__, __LINE__);
|
|
497 }
|
|
498 debug (selector)
|
|
499 Stdout.print("--- Restarting select() after being interrupted\n");
|
|
500 }
|
|
501 }
|
|
502 }
|
|
503 else
|
|
504 {
|
|
505 // Windows returns an error when select() is called with all three
|
|
506 // handle sets empty, so we emulate the POSIX behavior by calling
|
|
507 // Thread.sleep().
|
|
508 if (handlesAvailable)
|
|
509 {
|
|
510 toTimeval(&tv, timeout);
|
|
511
|
|
512 // FIXME: Can a system call be interrupted on Windows?
|
|
513 _eventCount = .select(ISelectable.Handle.max, readfds, writefds, exceptfds, timeout is TimeSpan.max ? null : &tv);
|
|
514
|
|
515 debug (selector)
|
|
516 Stdout.format("--- .select() returned {0}\n", _eventCount);
|
|
517 }
|
|
518 else
|
|
519 {
|
|
520 Thread.sleep(timeout.interval());
|
|
521 _eventCount = 0;
|
|
522 }
|
|
523 }
|
|
524 return _eventCount;
|
|
525 }
|
|
526
|
|
527 /**
|
|
528 * Return the selection set resulting from the call to any of the
|
|
529 * select() methods.
|
|
530 *
|
|
531 * Remarks:
|
|
532 * If the call to select() was unsuccessful or it did not return any
|
|
533 * events, the returned value will be null.
|
|
534 */
|
|
535 public ISelectionSet selectedSet()
|
|
536 {
|
|
537 return (_eventCount > 0 ? new SelectSelectionSet(_keys, cast(uint) _eventCount, _selectedReadSet,
|
|
538 _selectedWriteSet, _selectedExceptionSet) : null);
|
|
539 }
|
|
540
|
|
541 /**
|
|
542 * Return the selection key resulting from the registration of a
|
|
543 * conduit to the selector.
|
|
544 *
|
|
545 * Remarks:
|
|
546 * If the conduit is not registered to the selector the returned
|
|
547 * value will be null. No exception will be thrown by this method.
|
|
548 */
|
|
549 public SelectionKey key(ISelectable conduit)
|
|
550 {
|
|
551 return (conduit !is null ? _keys[conduit.fileHandle()] : null);
|
|
552 }
|
|
553 }
|
|
554
|
|
555 /**
|
|
556 * SelectionSet for the select()-based Selector.
|
|
557 */
|
|
558 private class SelectSelectionSet: ISelectionSet
|
|
559 {
|
|
560 private SelectionKey[ISelectable.Handle] _keys;
|
|
561 private uint _eventCount;
|
|
562 private HandleSet _readSet;
|
|
563 private HandleSet _writeSet;
|
|
564 private HandleSet _exceptionSet;
|
|
565
|
|
566 protected this(SelectionKey[ISelectable.Handle] keys, uint eventCount,
|
|
567 HandleSet readSet, HandleSet writeSet, HandleSet exceptionSet)
|
|
568 {
|
|
569 _keys = keys;
|
|
570 _eventCount = eventCount;
|
|
571 _readSet = readSet;
|
|
572 _writeSet = writeSet;
|
|
573 _exceptionSet = exceptionSet;
|
|
574 }
|
|
575
|
|
576 public uint length()
|
|
577 {
|
|
578 return _eventCount;
|
|
579 }
|
|
580
|
|
581 public int opApply(int delegate(inout SelectionKey) dg)
|
|
582 {
|
|
583 int rc = 0;
|
|
584 ISelectable.Handle handle;
|
|
585 Event events;
|
|
586
|
|
587 debug (selector)
|
|
588 Stdout.format("--- SelectSelectionSet.opApply() ({0} elements)\n", _eventCount);
|
|
589
|
|
590 foreach (SelectionKey current; _keys)
|
|
591 {
|
|
592 handle = current.conduit.fileHandle();
|
|
593
|
|
594 if (_readSet !is null && _readSet.isSet(handle))
|
|
595 events = Event.Read;
|
|
596 else
|
|
597 events = Event.None;
|
|
598
|
|
599 if (_writeSet !is null && _writeSet.isSet(handle))
|
|
600 events |= Event.Write;
|
|
601
|
|
602 if (_exceptionSet !is null && _exceptionSet.isSet(handle))
|
|
603 events |= Event.Error;
|
|
604
|
|
605 // Only invoke the delegate if there is an event for the conduit.
|
|
606 if (events != Event.None)
|
|
607 {
|
|
608 current.events = events;
|
|
609
|
|
610 debug (selector)
|
|
611 Stdout.format("--- Calling foreach delegate with selection key ({0}, 0x{1:x})\n",
|
|
612 cast(int) handle, cast(uint) events);
|
|
613
|
|
614 if (dg(current) != 0)
|
|
615 {
|
|
616 rc = -1;
|
|
617 break;
|
|
618 }
|
|
619 }
|
|
620 else
|
|
621 {
|
|
622 debug (selector)
|
|
623 Stdout.format("--- Handle {0} doesn't have pending events\n",
|
|
624 cast(int) handle);
|
|
625 }
|
|
626 }
|
|
627 return rc;
|
|
628 }
|
|
629 }
|
|
630
|
|
631
|
|
632 version (Windows)
|
|
633 {
|
|
634 /**
|
|
635 * Helper class used by the select()-based Selector to store handles.
|
|
636 * On Windows the handles are kept in an array of uints and the first
|
|
637 * element of the array stores the array "length" (i.e. number of handles
|
|
638 * in the array). Everything is stored so that the native select() API
|
|
639 * can use the HandleSet without additional conversions by just casting it
|
|
640 * to a fd_set*.
|
|
641 */
|
|
642 private class HandleSet
|
|
643 {
|
|
644 /** Default number of handles that will be held in the HandleSet. */
|
|
645 public const uint DefaultSize = 63;
|
|
646
|
|
647 private uint[] _buffer;
|
|
648
|
|
649 /**
|
|
650 * Constructor. Sets the initial number of handles that will be held
|
|
651 * in the HandleSet.
|
|
652 */
|
|
653 public this(uint size = DefaultSize)
|
|
654 {
|
|
655 _buffer = new uint[1 + size];
|
|
656 _buffer[0] = 0;
|
|
657 }
|
|
658
|
|
659 /**
|
|
660 * Return the number of handles present in the HandleSet.
|
|
661 */
|
|
662 public uint length()
|
|
663 {
|
|
664 return _buffer[0];
|
|
665 }
|
|
666
|
|
667 /**
|
|
668 * Remove all the handles from the set.
|
|
669 */
|
|
670 private void reset()
|
|
671 {
|
|
672 _buffer[0] = 0;
|
|
673 }
|
|
674
|
|
675 /**
|
|
676 * Add the handle to the set.
|
|
677 */
|
|
678 public void set(ISelectable.Handle handle)
|
|
679 in
|
|
680 {
|
|
681 assert(handle >= 0);
|
|
682 }
|
|
683 body
|
|
684 {
|
|
685 if (!isSet(handle))
|
|
686 {
|
|
687 // If we added too many sockets we increment the size of the buffer
|
|
688 if (++_buffer[0] >= _buffer.length)
|
|
689 {
|
|
690 _buffer.length = _buffer[0] + 1;
|
|
691 }
|
|
692 _buffer[_buffer[0]] = cast(uint) handle;
|
|
693 }
|
|
694 }
|
|
695
|
|
696 /**
|
|
697 * Remove the handle from the set.
|
|
698 */
|
|
699 public void clear(ISelectable.Handle handle)
|
|
700 {
|
|
701 for (uint i = 1; i <= _buffer[0]; ++i)
|
|
702 {
|
|
703 if (_buffer[i] == cast(uint) handle)
|
|
704 {
|
|
705 // We don't need to keep the handles in the order in which
|
|
706 // they were inserted, so we optimize the removal by
|
|
707 // copying the last element to the position of the removed
|
|
708 // element.
|
|
709 if (i != _buffer[0])
|
|
710 {
|
|
711 _buffer[i] = _buffer[_buffer[0]];
|
|
712 }
|
|
713 _buffer[0]--;
|
|
714 return;
|
|
715 }
|
|
716 }
|
|
717 }
|
|
718
|
|
719 /**
|
|
720 * Copy the contents of the HandleSet into this instance.
|
|
721 */
|
|
722 private HandleSet copy(HandleSet handleSet)
|
|
723 {
|
|
724 if (handleSet !is null)
|
|
725 {
|
|
726 _buffer[] = handleSet._buffer[];
|
|
727 }
|
|
728 else
|
|
729 {
|
|
730 _buffer = null;
|
|
731 }
|
|
732 return this;
|
|
733 }
|
|
734
|
|
735 /**
|
|
736 * Check whether the handle has been set.
|
|
737 */
|
|
738 public bool isSet(ISelectable.Handle handle)
|
|
739 {
|
|
740 uint* start;
|
|
741 uint* stop;
|
|
742
|
|
743 for (start = _buffer.ptr + 1, stop = start + _buffer[0]; start != stop; start++)
|
|
744 {
|
|
745 if (*start == cast(uint) handle)
|
|
746 return true;
|
|
747 }
|
|
748 return false;
|
|
749 }
|
|
750
|
|
751 /**
|
|
752 * Cast the current object to a pointer to an fd_set, to be used with the
|
|
753 * select() system call.
|
|
754 */
|
|
755 public fd_set* opCast()
|
|
756 {
|
|
757 return cast(fd_set*) _buffer.ptr;
|
|
758 }
|
|
759
|
|
760
|
|
761 debug (selector)
|
|
762 {
|
|
763 /**
|
|
764 * Dump the contents of a HandleSet into stdout.
|
|
765 */
|
|
766 void dump(char[] name = null)
|
|
767 {
|
|
768 if (_buffer !is null && _buffer.length > 0 && _buffer[0] > 0)
|
|
769 {
|
|
770 char[] handleStr = new char[16];
|
|
771 char[] handleListStr;
|
|
772 bool isFirst = true;
|
|
773
|
|
774 if (name is null)
|
|
775 {
|
|
776 name = "HandleSet";
|
|
777 }
|
|
778
|
|
779 for (uint i = 1; i < _buffer[0]; ++i)
|
|
780 {
|
|
781 if (!isFirst)
|
|
782 {
|
|
783 handleListStr ~= ", ";
|
|
784 }
|
|
785 else
|
|
786 {
|
|
787 isFirst = false;
|
|
788 }
|
|
789
|
|
790 handleListStr ~= itoa(handleStr, _buffer[i]);
|
|
791 }
|
|
792
|
|
793 Stdout.formatln("--- {0}[{1}]: {2}", name, _buffer[0], handleListStr);
|
|
794 }
|
|
795 }
|
|
796 }
|
|
797 }
|
|
798 }
|
|
799 else version (Posix)
|
|
800 {
|
|
801 private import tango.core.BitManip;
|
|
802
|
|
803 /**
|
|
804 * Helper class used by the select()-based Selector to store handles.
|
|
805 * On POSIX-compatible platforms the handles are kept in an array of bits.
|
|
806 * Everything is stored so that the native select() API can use the
|
|
807 * HandleSet without additional conversions by casting it to a fd_set*.
|
|
808 */
|
|
809 private class HandleSet
|
|
810 {
|
|
811 /** Default number of handles that will be held in the HandleSet. */
|
|
812 const uint DefaultSize = 1024;
|
|
813 /** Number of bits per element held in the _buffer */
|
|
814 const uint BitsPerElement = uint.sizeof * 8;
|
|
815
|
|
816 private uint[] _buffer;
|
|
817
|
|
818 /**
|
|
819 * Constructor. Sets the initial number of handles that will be held
|
|
820 * in the HandleSet.
|
|
821 */
|
|
822 protected this(uint size = DefaultSize)
|
|
823 {
|
|
824 uint count;
|
|
825
|
|
826 if (size < 1024)
|
|
827 size = 1024;
|
|
828
|
|
829 count = size / BitsPerElement;
|
|
830 if (size % BitsPerElement != 0)
|
|
831 count++;
|
|
832 _buffer = new uint[count];
|
|
833 }
|
|
834
|
|
835 /**
|
|
836 * Return the number of handles present in the HandleSet.
|
|
837 */
|
|
838 public uint length()
|
|
839 {
|
|
840 return _buffer.length;
|
|
841 }
|
|
842
|
|
843 /**
|
|
844 * Remove all the handles from the set.
|
|
845 */
|
|
846 public void reset()
|
|
847 {
|
|
848 _buffer[] = 0;
|
|
849 }
|
|
850
|
|
851 /**
|
|
852 * Add a handle to the set.
|
|
853 */
|
|
854 public void set(ISelectable.Handle handle)
|
|
855 {
|
|
856 // If we added too many sockets we increment the size of the buffer
|
|
857 if (cast(uint) handle >= BitsPerElement * _buffer.length)
|
|
858 {
|
|
859 _buffer.length = cast(uint) handle + 1;
|
|
860 }
|
|
861 bts(&_buffer[elementOffset(handle)], bitOffset(handle));
|
|
862 }
|
|
863
|
|
864 /**
|
|
865 * Remove a handle from the set.
|
|
866 */
|
|
867 public void clear(ISelectable.Handle handle)
|
|
868 {
|
|
869 btr(&_buffer[elementOffset(handle)], bitOffset(handle));
|
|
870 }
|
|
871
|
|
872 /**
|
|
873 * Copy the contents of the HandleSet into this instance.
|
|
874 */
|
|
875 private HandleSet copy(HandleSet handleSet)
|
|
876 {
|
|
877 if (handleSet !is null)
|
|
878 {
|
|
879 _buffer[] = handleSet._buffer[];
|
|
880 }
|
|
881 else
|
|
882 {
|
|
883 _buffer = null;
|
|
884 }
|
|
885 return this;
|
|
886 }
|
|
887
|
|
888 /**
|
|
889 * Check whether the handle has been set.
|
|
890 */
|
|
891 public bool isSet(ISelectable.Handle handle)
|
|
892 {
|
|
893 return (bt(&_buffer[elementOffset(handle)], bitOffset(handle)) != 0);
|
|
894 }
|
|
895
|
|
896 /**
|
|
897 * Cast the current object to a pointer to an fd_set, to be used with the
|
|
898 * select() system call.
|
|
899 */
|
|
900 public fd_set* opCast()
|
|
901 {
|
|
902 return cast(fd_set*) _buffer;
|
|
903 }
|
|
904
|
|
905 /**
|
|
906 * Calculate the offset (in uints) of a handle in the set.
|
|
907 */
|
|
908 private static uint elementOffset(ISelectable.Handle handle)
|
|
909 {
|
|
910 return cast(uint) handle / BitsPerElement;
|
|
911 }
|
|
912
|
|
913 /**
|
|
914 * Calculate the offset of the bit corresponding to a handle in the set.
|
|
915 */
|
|
916 private static uint bitOffset(ISelectable.Handle handle)
|
|
917 {
|
|
918 return cast(uint) handle % BitsPerElement;
|
|
919 }
|
|
920
|
|
921 debug (selector)
|
|
922 {
|
|
923 /**
|
|
924 * Dump the contents of a HandleSet into stdout.
|
|
925 */
|
|
926 void dump(char[] name = null)
|
|
927 {
|
|
928 if (_buffer !is null && _buffer.length > 0)
|
|
929 {
|
|
930 char[] handleStr = new char[16];
|
|
931 char[] handleListStr;
|
|
932 bool isFirst = true;
|
|
933
|
|
934 if (name is null)
|
|
935 {
|
|
936 name = "HandleSet";
|
|
937 }
|
|
938
|
|
939 for (uint i = 0; i < _buffer.length * _buffer[0].sizeof; ++i)
|
|
940 {
|
|
941 if (isSet(cast(ISelectable.Handle) i))
|
|
942 {
|
|
943 if (!isFirst)
|
|
944 {
|
|
945 handleListStr ~= ", ";
|
|
946 }
|
|
947 else
|
|
948 {
|
|
949 isFirst = false;
|
|
950 }
|
|
951 handleListStr ~= itoa(handleStr, i);
|
|
952 }
|
|
953 }
|
|
954 Stdout.formatln("--- {0}: {1}", name, handleListStr);
|
|
955 }
|
|
956 }
|
|
957 }
|
|
958 }
|
|
959 }
|