View Javadoc

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 }