1 // ========================================================================
2 // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
3 // ------------------------------------------------------------------------
4 // All rights reserved. This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v1.0
6 // and Apache License v2.0 which accompanies this distribution.
7 // The Eclipse Public License is available at
8 // http://www.eclipse.org/legal/epl-v10.html
9 // The Apache License v2.0 is available at
10 // http://www.opensource.org/licenses/apache2.0.php
11 // You may elect to redistribute this code under either of these licenses.
12 // ========================================================================
13
14 package org.eclipse.jetty.io.nio;
15
16 import java.io.IOException;
17 import java.nio.channels.CancelledKeyException;
18 import java.nio.channels.SelectableChannel;
19 import java.nio.channels.SelectionKey;
20 import java.nio.channels.Selector;
21 import java.nio.channels.ServerSocketChannel;
22 import java.nio.channels.SocketChannel;
23 import java.util.concurrent.ConcurrentHashMap;
24 import java.util.concurrent.ConcurrentLinkedQueue;
25 import java.util.concurrent.ConcurrentMap;
26 import java.util.concurrent.CountDownLatch;
27 import java.util.concurrent.TimeUnit;
28
29 import org.eclipse.jetty.io.ConnectedEndPoint;
30 import org.eclipse.jetty.io.Connection;
31 import org.eclipse.jetty.io.EndPoint;
32 import org.eclipse.jetty.util.component.AbstractLifeCycle;
33 import org.eclipse.jetty.util.log.Log;
34 import org.eclipse.jetty.util.thread.Timeout;
35 import org.eclipse.jetty.util.thread.Timeout.Task;
36
37
38 /* ------------------------------------------------------------ */
39 /**
40 * The Selector Manager manages and number of SelectSets to allow
41 * NIO scheduling to scale to large numbers of connections.
42 * <p>
43 * This class works around a number of know JVM bugs. For details
44 * see http://wiki.eclipse.org/Jetty/Feature/JVM_NIO_Bug
45 */
46 public abstract class SelectorManager extends AbstractLifeCycle
47 {
48 // TODO Tune these by approx system speed.
49 private static final int __JVMBUG_THRESHHOLD=Integer.getInteger("org.eclipse.jetty.io.nio.JVMBUG_THRESHHOLD",512).intValue();
50 private static final int __MONITOR_PERIOD=Integer.getInteger("org.eclipse.jetty.io.nio.MONITOR_PERIOD",1000).intValue();
51 private static final int __MAX_SELECTS=Integer.getInteger("org.eclipse.jetty.io.nio.MAX_SELECTS",25000).intValue();
52 private static final int __BUSY_PAUSE=Integer.getInteger("org.eclipse.jetty.io.nio.BUSY_PAUSE",50).intValue();
53 private static final int __BUSY_KEY=Integer.getInteger("org.eclipse.jetty.io.nio.BUSY_KEY",-1).intValue();
54 private static final int __IDLE_TICK=Integer.getInteger("org.eclipse.jetty.io.nio.IDLE_TICK",400).intValue();
55
56 private int _maxIdleTime;
57 private int _lowResourcesMaxIdleTime;
58 private long _lowResourcesConnections;
59 private SelectSet[] _selectSet;
60 private int _selectSets=1;
61 private volatile int _set;
62
63 /* ------------------------------------------------------------ */
64 /**
65 * @param maxIdleTime The maximum period in milli seconds that a connection may be idle before it is closed.
66 * @see #setLowResourcesMaxIdleTime(long)
67 */
68 public void setMaxIdleTime(long maxIdleTime)
69 {
70 _maxIdleTime=(int)maxIdleTime;
71 }
72
73 /* ------------------------------------------------------------ */
74 /**
75 * @param selectSets number of select sets to create
76 */
77 public void setSelectSets(int selectSets)
78 {
79 long lrc = _lowResourcesConnections * _selectSets;
80 _selectSets=selectSets;
81 _lowResourcesConnections=lrc/_selectSets;
82 }
83
84 /* ------------------------------------------------------------ */
85 /**
86 * @return the max idle time
87 */
88 public long getMaxIdleTime()
89 {
90 return _maxIdleTime;
91 }
92
93 /* ------------------------------------------------------------ */
94 /**
95 * @return the number of select sets in use
96 */
97 public int getSelectSets()
98 {
99 return _selectSets;
100 }
101
102 /* ------------------------------------------------------------ */
103 /**
104 * @param i
105 * @return The select set
106 */
107 public SelectSet getSelectSet(int i)
108 {
109 return _selectSet[i];
110 }
111
112 /* ------------------------------------------------------------ */
113 /** Register a channel
114 * @param channel
115 * @param att Attached Object
116 */
117 public void register(SocketChannel channel, Object att)
118 {
119 // The ++ increment here is not atomic, but it does not matter.
120 // so long as the value changes sometimes, then connections will
121 // be distributed over the available sets.
122
123 int s=_set++;
124 s=s%_selectSets;
125 SelectSet[] sets=_selectSet;
126 if (sets!=null)
127 {
128 SelectSet set=sets[s];
129 set.addChange(channel,att);
130 set.wakeup();
131 }
132 }
133
134
135 /* ------------------------------------------------------------ */
136 /** Register a channel
137 * @param channel
138 * @param att Attached Object
139 */
140 public void register(SocketChannel channel)
141 {
142 // The ++ increment here is not atomic, but it does not matter.
143 // so long as the value changes sometimes, then connections will
144 // be distributed over the available sets.
145
146 int s=_set++;
147 s=s%_selectSets;
148 SelectSet[] sets=_selectSet;
149 if (sets!=null)
150 {
151 SelectSet set=sets[s];
152 set.addChange(channel);
153 set.wakeup();
154 }
155 }
156
157 /* ------------------------------------------------------------ */
158 /** Register a {@link ServerSocketChannel}
159 * @param acceptChannel
160 */
161 public void register(ServerSocketChannel acceptChannel)
162 {
163 int s=_set++;
164 s=s%_selectSets;
165 SelectSet set=_selectSet[s];
166 set.addChange(acceptChannel);
167 set.wakeup();
168 }
169
170 /* ------------------------------------------------------------ */
171 /**
172 * @return the lowResourcesConnections
173 */
174 public long getLowResourcesConnections()
175 {
176 return _lowResourcesConnections*_selectSets;
177 }
178
179 /* ------------------------------------------------------------ */
180 /**
181 * Set the number of connections, which if exceeded places this manager in low resources state.
182 * This is not an exact measure as the connection count is averaged over the select sets.
183 * @param lowResourcesConnections the number of connections
184 * @see #setLowResourcesMaxIdleTime(long)
185 */
186 public void setLowResourcesConnections(long lowResourcesConnections)
187 {
188 _lowResourcesConnections=(lowResourcesConnections+_selectSets-1)/_selectSets;
189 }
190
191 /* ------------------------------------------------------------ */
192 /**
193 * @return the lowResourcesMaxIdleTime
194 */
195 public long getLowResourcesMaxIdleTime()
196 {
197 return _lowResourcesMaxIdleTime;
198 }
199
200 /* ------------------------------------------------------------ */
201 /**
202 * @param lowResourcesMaxIdleTime the period in ms that a connection is allowed to be idle when this SelectSet has more connections than {@link #getLowResourcesConnections()}
203 * @see #setMaxIdleTime(long)
204 */
205 public void setLowResourcesMaxIdleTime(long lowResourcesMaxIdleTime)
206 {
207 _lowResourcesMaxIdleTime=(int)lowResourcesMaxIdleTime;
208 }
209
210 /* ------------------------------------------------------------ */
211 /**
212 * @param acceptorID
213 * @throws IOException
214 */
215 public void doSelect(int acceptorID) throws IOException
216 {
217 SelectSet[] sets= _selectSet;
218 if (sets!=null && sets.length>acceptorID && sets[acceptorID]!=null)
219 sets[acceptorID].doSelect();
220 }
221
222 /* ------------------------------------------------------------------------------- */
223 public abstract boolean dispatch(Runnable task);
224
225 /* ------------------------------------------------------------ */
226 /* (non-Javadoc)
227 * @see org.eclipse.component.AbstractLifeCycle#doStart()
228 */
229 @Override
230 protected void doStart() throws Exception
231 {
232 _selectSet = new SelectSet[_selectSets];
233 for (int i=0;i<_selectSet.length;i++)
234 _selectSet[i]= new SelectSet(i);
235
236 super.doStart();
237 }
238
239
240 /* ------------------------------------------------------------------------------- */
241 @Override
242 protected void doStop() throws Exception
243 {
244 SelectSet[] sets= _selectSet;
245 _selectSet=null;
246 if (sets!=null)
247 {
248 for (SelectSet set : sets)
249 {
250 if (set!=null)
251 set.stop();
252 }
253 }
254 super.doStop();
255 }
256
257 /* ------------------------------------------------------------ */
258 /**
259 * @param endpoint
260 */
261 protected abstract void endPointClosed(SelectChannelEndPoint endpoint);
262
263 /* ------------------------------------------------------------ */
264 /**
265 * @param endpoint
266 */
267 protected abstract void endPointOpened(SelectChannelEndPoint endpoint);
268
269 /* ------------------------------------------------------------ */
270 protected abstract void endPointUpgraded(ConnectedEndPoint endpoint,Connection oldConnection);
271
272 /* ------------------------------------------------------------------------------- */
273 protected abstract Connection newConnection(SocketChannel channel, SelectChannelEndPoint endpoint);
274
275 /* ------------------------------------------------------------ */
276 /**
277 * Create a new end point
278 * @param channel
279 * @param selectSet
280 * @param sKey the selection key
281 * @return the new endpoint {@link SelectChannelEndPoint}
282 * @throws IOException
283 */
284 protected abstract SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey sKey) throws IOException;
285
286 /* ------------------------------------------------------------------------------- */
287 public void dump()
288 {
289 for (final SelectSet set :_selectSet)
290 {
291 Thread selecting = set._selecting;
292 Log.info("SelectSet "+set._setID+" : "+selecting);
293 if (selecting!=null)
294 {
295 StackTraceElement[] trace =selecting.getStackTrace();
296 if (trace!=null)
297 {
298 for (StackTraceElement e : trace)
299 {
300 Log.info("\tat "+e.toString());
301 }
302 }
303 }
304
305 set.addChange(new Runnable(){
306 public void run()
307 {
308 set.dump();
309 }
310 });
311 }
312 }
313
314
315 /* ------------------------------------------------------------------------------- */
316 /* ------------------------------------------------------------------------------- */
317 /* ------------------------------------------------------------------------------- */
318 public class SelectSet
319 {
320 private final int _setID;
321 private final Timeout _timeout;
322
323 private final ConcurrentLinkedQueue<Object> _changes = new ConcurrentLinkedQueue<Object>();
324
325 private Selector _selector;
326
327 private volatile Thread _selecting;
328 private int _jvmBug;
329 private int _selects;
330 private long _monitorStart;
331 private long _monitorNext;
332 private boolean _pausing;
333 private SelectionKey _busyKey;
334 private int _busyKeyCount;
335 private long _log;
336 private int _paused;
337 private int _jvmFix0;
338 private int _jvmFix1;
339 private int _jvmFix2;
340 private volatile long _idleTick;
341 private ConcurrentMap<SelectChannelEndPoint,Object> _endPoints = new ConcurrentHashMap<SelectChannelEndPoint, Object>();
342
343 /* ------------------------------------------------------------ */
344 SelectSet(int acceptorID) throws Exception
345 {
346 _setID=acceptorID;
347
348 _idleTick = System.currentTimeMillis();
349 _timeout = new Timeout(this);
350 _timeout.setDuration(0L);
351
352 // create a selector;
353 _selector = Selector.open();
354 _monitorStart=System.currentTimeMillis();
355 _monitorNext=_monitorStart+__MONITOR_PERIOD;
356 _log=_monitorStart+60000;
357 }
358
359 /* ------------------------------------------------------------ */
360 public void addChange(Object change)
361 {
362 _changes.add(change);
363 }
364
365 /* ------------------------------------------------------------ */
366 public void addChange(SelectableChannel channel, Object att)
367 {
368 if (att==null)
369 addChange(channel);
370 else if (att instanceof EndPoint)
371 addChange(att);
372 else
373 addChange(new ChannelAndAttachment(channel,att));
374 }
375
376 /* ------------------------------------------------------------ */
377 /**
378 * Select and dispatch tasks found from changes and the selector.
379 *
380 * @throws IOException
381 */
382 public void doSelect() throws IOException
383 {
384 try
385 {
386 _selecting=Thread.currentThread();
387 final Selector selector=_selector;
388
389 // Make any key changes required
390 Object change;
391 int changes=_changes.size();
392 while (changes-->0 && (change=_changes.poll())!=null)
393 {
394 try
395 {
396 if (change instanceof EndPoint)
397 {
398 // Update the operations for a key.
399 SelectChannelEndPoint endpoint = (SelectChannelEndPoint)change;
400 endpoint.doUpdateKey();
401 }
402 else if (change instanceof ChannelAndAttachment)
403 {
404 // finish accepting/connecting this connection
405 final ChannelAndAttachment asc = (ChannelAndAttachment)change;
406 final SelectableChannel channel=asc._channel;
407 final Object att = asc._attachment;
408 SelectionKey key = channel.register(selector,SelectionKey.OP_READ,att);
409 SelectChannelEndPoint endpoint = createEndPoint((SocketChannel)channel,key);
410 key.attach(endpoint);
411 endpoint.schedule();
412 }
413 else if (change instanceof SocketChannel)
414 {
415 // Newly registered channel
416 final SocketChannel channel=(SocketChannel)change;
417 SelectionKey key = channel.register(selector,SelectionKey.OP_READ,null);
418 SelectChannelEndPoint endpoint = createEndPoint(channel,key);
419 key.attach(endpoint);
420 endpoint.schedule();
421 }
422 else if (change instanceof Runnable)
423 {
424 dispatch((Runnable)change);
425 }
426 else
427 throw new IllegalArgumentException(change.toString());
428 }
429 catch (Exception e)
430 {
431 if (isRunning())
432 Log.warn(e);
433 else
434 Log.debug(e);
435 }
436 catch (Error e)
437 {
438 if (isRunning())
439 Log.warn(e);
440 else
441 Log.debug(e);
442 }
443 }
444
445
446 // Do and instant select to see if any connections can be handled.
447 int selected=selector.selectNow();
448 _selects++;
449
450 long now=System.currentTimeMillis();
451
452 // if no immediate things to do
453 if (selected==0)
454 {
455 // If we are in pausing mode
456 if (_pausing)
457 {
458 try
459 {
460 Thread.sleep(__BUSY_PAUSE); // pause to reduce impact of busy loop
461 }
462 catch(InterruptedException e)
463 {
464 Log.ignore(e);
465 }
466 now=System.currentTimeMillis();
467 }
468
469 // workout how long to wait in select
470 _timeout.setNow(now);
471 long to_next_timeout=_timeout.getTimeToNext();
472
473 long wait = _changes.size()==0?__IDLE_TICK:0L;
474 if (wait > 0 && to_next_timeout >= 0 && wait > to_next_timeout)
475 wait = to_next_timeout;
476
477 // If we should wait with a select
478 if (wait>0)
479 {
480 long before=now;
481 selected=selector.select(wait);
482 _selects++;
483 now = System.currentTimeMillis();
484 _timeout.setNow(now);
485 checkJvmBugs(before, now, wait, selected);
486 }
487 }
488
489 // have we been destroyed while sleeping
490 if (_selector==null || !selector.isOpen())
491 return;
492
493 // Look for things to do
494 for (SelectionKey key: selector.selectedKeys())
495 {
496 try
497 {
498 if (!key.isValid())
499 {
500 key.cancel();
501 SelectChannelEndPoint endpoint = (SelectChannelEndPoint)key.attachment();
502 if (endpoint != null)
503 endpoint.doUpdateKey();
504 continue;
505 }
506
507 Object att = key.attachment();
508 if (att instanceof SelectChannelEndPoint)
509 {
510 ((SelectChannelEndPoint)att).schedule();
511 }
512 else
513 {
514 // Wrap readable registered channel in an endpoint
515 SocketChannel channel = (SocketChannel)key.channel();
516 SelectChannelEndPoint endpoint = createEndPoint(channel,key);
517 key.attach(endpoint);
518 if (key.isReadable())
519 endpoint.schedule();
520 }
521 key = null;
522 }
523 catch (CancelledKeyException e)
524 {
525 Log.ignore(e);
526 }
527 catch (Exception e)
528 {
529 if (isRunning())
530 Log.warn(e);
531 else
532 Log.ignore(e);
533
534 if (key != null && !(key.channel() instanceof ServerSocketChannel) && key.isValid())
535 key.cancel();
536 }
537 }
538
539 // Everything always handled
540 selector.selectedKeys().clear();
541
542 now=System.currentTimeMillis();
543 _timeout.setNow(now);
544 Task task = _timeout.expired();
545 while (task!=null)
546 {
547 if (task instanceof Runnable)
548 dispatch((Runnable)task);
549 task = _timeout.expired();
550 }
551
552 // Idle tick
553 if (now-_idleTick>__IDLE_TICK)
554 {
555 _idleTick=now;
556
557 final long idle_now=((_lowResourcesConnections>0 && selector.keys().size()>_lowResourcesConnections))
558 ?(now+_maxIdleTime-_lowResourcesMaxIdleTime)
559 :now;
560
561 dispatch(new Runnable()
562 {
563 public void run()
564 {
565 for (SelectChannelEndPoint endp:_endPoints.keySet())
566 {
567 endp.checkIdleTimestamp(idle_now);
568 }
569 }
570 });
571 }
572 }
573 catch (CancelledKeyException e)
574 {
575 Log.ignore(e);
576 }
577 finally
578 {
579 _selecting=null;
580 }
581 }
582
583 /* ------------------------------------------------------------ */
584 private void checkJvmBugs(long before, long now, long wait, int selected)
585 throws IOException
586 {
587 Selector selector = _selector;
588 if (selector==null)
589 return;
590
591 // Look for JVM bugs over a monitor period.
592 // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6403933
593 // http://bugs.sun.com/view_bug.do?bug_id=6693490
594 if (now>_monitorNext)
595 {
596 _selects=(int)(_selects*__MONITOR_PERIOD/(now-_monitorStart));
597 _pausing=_selects>__MAX_SELECTS;
598 if (_pausing)
599 _paused++;
600
601 _selects=0;
602 _jvmBug=0;
603 _monitorStart=now;
604 _monitorNext=now+__MONITOR_PERIOD;
605 }
606
607 if (now>_log)
608 {
609 if (_paused>0)
610 Log.debug(this+" Busy selector - injecting delay "+_paused+" times");
611
612 if (_jvmFix2>0)
613 Log.debug(this+" JVM BUG(s) - injecting delay"+_jvmFix2+" times");
614
615 if (_jvmFix1>0)
616 Log.debug(this+" JVM BUG(s) - recreating selector "+_jvmFix1+" times, cancelled keys "+_jvmFix0+" times");
617
618 else if(Log.isDebugEnabled() && _jvmFix0>0)
619 Log.debug(this+" JVM BUG(s) - cancelled keys "+_jvmFix0+" times");
620 _paused=0;
621 _jvmFix2=0;
622 _jvmFix1=0;
623 _jvmFix0=0;
624 _log=now+60000;
625 }
626
627 // If we see signature of possible JVM bug, increment count.
628 if (selected==0 && wait>10 && (now-before)<(wait/2))
629 {
630 // Increment bug count and try a work around
631 _jvmBug++;
632 if (_jvmBug>(__JVMBUG_THRESHHOLD))
633 {
634 try
635 {
636 if (_jvmBug==__JVMBUG_THRESHHOLD+1)
637 _jvmFix2++;
638
639 Thread.sleep(__BUSY_PAUSE); // pause to avoid busy loop
640 }
641 catch(InterruptedException e)
642 {
643 Log.ignore(e);
644 }
645 }
646 else if (_jvmBug==__JVMBUG_THRESHHOLD)
647 {
648 synchronized (this)
649 {
650 // BLOODY SUN BUG !!! Try refreshing the entire selector.
651 final Selector new_selector = Selector.open();
652 for (SelectionKey k: selector.keys())
653 {
654 if (!k.isValid() || k.interestOps()==0)
655 continue;
656
657 final SelectableChannel channel = k.channel();
658 final Object attachment = k.attachment();
659
660 if (attachment==null)
661 addChange(channel);
662 else
663 addChange(channel,attachment);
664 }
665 _selector.close();
666 _selector=new_selector;
667 return;
668 }
669 }
670 else if (_jvmBug%32==31) // heuristic attempt to cancel key 31,63,95,... loops
671 {
672 // Cancel keys with 0 interested ops
673 int cancelled=0;
674 for (SelectionKey k: selector.keys())
675 {
676 if (k.isValid()&&k.interestOps()==0)
677 {
678 k.cancel();
679 cancelled++;
680 }
681 }
682 if (cancelled>0)
683 _jvmFix0++;
684
685 return;
686 }
687 }
688 else if (__BUSY_KEY>0 && selected==1 && _selects>__MAX_SELECTS)
689 {
690 // Look for busy key
691 SelectionKey busy = selector.selectedKeys().iterator().next();
692 if (busy==_busyKey)
693 {
694 if (++_busyKeyCount>__BUSY_KEY && !(busy.channel() instanceof ServerSocketChannel))
695 {
696 final SelectChannelEndPoint endpoint = (SelectChannelEndPoint)busy.attachment();
697 Log.warn("Busy Key "+busy.channel()+" "+endpoint);
698 busy.cancel();
699 if (endpoint!=null)
700 {
701 dispatch(new Runnable()
702 {
703 public void run()
704 {
705 try
706 {
707 endpoint.close();
708 }
709 catch (IOException e)
710 {
711 Log.ignore(e);
712 }
713 }
714 });
715 }
716 }
717 }
718 else
719 _busyKeyCount=0;
720 _busyKey=busy;
721 }
722 }
723
724 /* ------------------------------------------------------------ */
725 public SelectorManager getManager()
726 {
727 return SelectorManager.this;
728 }
729
730 /* ------------------------------------------------------------ */
731 public long getNow()
732 {
733 return _timeout.getNow();
734 }
735
736 /* ------------------------------------------------------------ */
737 /**
738 * @param task The task to timeout. If it implements Runnable, then
739 * expired will be called from a dispatched thread.
740 *
741 * @param timeoutMs
742 */
743 public void scheduleTimeout(Timeout.Task task, long timeoutMs)
744 {
745 if (!(task instanceof Runnable))
746 throw new IllegalArgumentException("!Runnable");
747 _timeout.schedule(task, timeoutMs);
748 }
749
750 /* ------------------------------------------------------------ */
751 public void cancelTimeout(Timeout.Task task)
752 {
753 task.cancel();
754 }
755
756 /* ------------------------------------------------------------ */
757 public void wakeup()
758 {
759 Selector selector = _selector;
760 if (selector!=null)
761 selector.wakeup();
762 }
763
764 /* ------------------------------------------------------------ */
765 private SelectChannelEndPoint createEndPoint(SocketChannel channel, SelectionKey sKey) throws IOException
766 {
767 SelectChannelEndPoint endp = newEndPoint(channel,this,sKey);
768 endPointOpened(endp);
769 _endPoints.put(endp,this);
770 return endp;
771 }
772
773 /* ------------------------------------------------------------ */
774 public void destroyEndPoint(SelectChannelEndPoint endp)
775 {
776 _endPoints.remove(endp);
777 endPointClosed(endp);
778 }
779
780 /* ------------------------------------------------------------ */
781 Selector getSelector()
782 {
783 return _selector;
784 }
785
786 /* ------------------------------------------------------------ */
787 void stop() throws Exception
788 {
789 // Spin for a while waiting for selector to complete
790 // to avoid unneccessary closed channel exceptions
791 try
792 {
793 for (int i=0;i<100 && _selecting!=null;i++)
794 {
795 wakeup();
796 Thread.sleep(1);
797 }
798 }
799 catch(Exception e)
800 {
801 Log.ignore(e);
802 }
803
804 // close endpoints and selector
805 synchronized (this)
806 {
807 for (SelectionKey key:_selector.keys())
808 {
809 if (key==null)
810 continue;
811 Object att=key.attachment();
812 if (att instanceof EndPoint)
813 {
814 EndPoint endpoint = (EndPoint)att;
815 try
816 {
817 endpoint.close();
818 }
819 catch(IOException e)
820 {
821 Log.ignore(e);
822 }
823 }
824 }
825
826
827 _timeout.cancelAll();
828 try
829 {
830 if (_selector != null)
831 _selector.close();
832 }
833 catch (IOException e)
834 {
835 Log.ignore(e);
836 }
837 _selector=null;
838 }
839 }
840
841 public void dump()
842 {
843 synchronized (System.err)
844 {
845 Selector selector=_selector;
846 Log.info("SelectSet "+_setID+" "+selector.keys().size());
847 for (SelectionKey key: selector.keys())
848 {
849 if (key.isValid())
850 Log.info(key.channel()+" "+key.interestOps()+" "+key.readyOps()+" "+key.attachment());
851 else
852 Log.info(key.channel()+" - - "+key.attachment());
853 }
854 }
855 }
856
857 }
858
859 /* ------------------------------------------------------------ */
860 private static class ChannelAndAttachment
861 {
862 final SelectableChannel _channel;
863 final Object _attachment;
864
865 public ChannelAndAttachment(SelectableChannel channel, Object attachment)
866 {
867 super();
868 _channel = channel;
869 _attachment = attachment;
870 }
871 }
872
873 }