View Javadoc

1   /***
2    *
3    * Copyright 2003-2004 The Apache Software Foundation
4    *
5    *  Licensed under the Apache License, Version 2.0 (the "License");
6    *  you may not use this file except in compliance with the License.
7    *  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  
18  package org.activeio.net;
19  
20  import java.io.IOException;
21  import java.nio.channels.ClosedChannelException;
22  import java.nio.channels.SelectionKey;
23  import java.nio.channels.Selector;
24  import java.nio.channels.SocketChannel;
25  import java.util.Iterator;
26  import java.util.LinkedList;
27  import java.util.Set;
28  
29  import org.activeio.ChannelFactory;
30  
31  import EDU.oswego.cs.dl.util.concurrent.Executor;
32  import EDU.oswego.cs.dl.util.concurrent.DirectExecutor;
33  
34  /***
35   * The SelectorManager will manage one Selector and the thread that checks the
36   * selector.
37   * 
38   * We may need to consider running more than one thread to check the selector if
39   * servicing the selector takes too long.
40   * 
41   * @version $Rev: 46019 $ $Date: 2004-09-14 05:56:06 -0400 (Tue, 14 Sep 2004) $
42   */
43  final public class NIOAsynchChannelSelectorManager {
44  
45      static private Executor selectorExecutor = ChannelFactory.DEFAULT_EXECUTOR;
46      static private Executor channelExecutor = ChannelFactory.DEFAULT_EXECUTOR;
47      
48      static private LinkedList freeManagers = new LinkedList();
49      static private LinkedList fullManagers = new LinkedList();
50      private static final int MAX_CHANNELS_PER_SELECTOR  = 50;
51      
52      static {
53         String os = System.getProperty("os.name");
54         if( os.startsWith("Linux") ) {
55             channelExecutor = new DirectExecutor();
56         }
57      } 
58  
59      public static interface SelectorManagerListener {
60          public void onSelect(SocketChannelAsynchChannelSelection selector);
61      }
62  
63      final public class SocketChannelAsynchChannelSelection {
64          
65          private final SelectionKey key;
66          private final SelectorManagerListener listener;
67          private boolean closed;
68          private int interest;
69  
70          private SocketChannelAsynchChannelSelection(SocketChannel socketChannel, SelectorManagerListener listener)
71                  throws ClosedChannelException {
72              this.listener = listener;
73              this.key = socketChannel.register(selector, 0, this);
74              incrementUseCounter();
75          }
76  
77          public void setInterestOps(int ops) {
78              	if( closed ) 
79              		return;
80              	interest = ops;
81               enable();
82          }
83          
84          public void enable() {
85              if( closed ) 
86                  return;
87              key.interestOps(interest);
88              selector.wakeup();
89          }
90  
91          public void disable() {
92              if( closed ) 
93                  return;
94              key.interestOps(0);
95          }
96  
97          public void close() {
98          	if( closed ) 
99          		return;
100         	
101             key.cancel();
102             decrementUseCounter();
103             selector.wakeup();
104             closed=true;
105         }
106         
107         public void onSelect() {
108             if( !key.isValid() )
109                 return;
110             listener.onSelect(this);
111         }
112 
113         public boolean isWritable() {
114             return key.isWritable();
115         }
116 
117         public boolean isReadable() {
118             return key.isReadable();
119         }
120     }
121 
122     public synchronized static SocketChannelAsynchChannelSelection register(
123             SocketChannel socketChannel, SelectorManagerListener listener)
124             throws IOException {
125 
126         NIOAsynchChannelSelectorManager manager = null;
127         synchronized (freeManagers) {
128             if (freeManagers.size() > 0)
129                 manager = (NIOAsynchChannelSelectorManager) freeManagers.getFirst();
130             if (manager == null) {
131                 manager = new NIOAsynchChannelSelectorManager();
132                 freeManagers.addFirst(manager);
133             }
134 
135             // That manager may have filled up.
136             SocketChannelAsynchChannelSelection selection = manager.new SocketChannelAsynchChannelSelection(
137                     socketChannel, listener);
138             if (manager.useCounter >= MAX_CHANNELS_PER_SELECTOR) {
139                 freeManagers.removeFirst();
140                 fullManagers.addLast(manager);
141             }
142             return selection;
143         }
144     }
145 
146     public synchronized static void setSelectorExecutor(Executor executor) {
147         NIOAsynchChannelSelectorManager.selectorExecutor = executor;
148     }
149     
150     public synchronized static void setChannelExecutor(Executor executor) {
151         NIOAsynchChannelSelectorManager.channelExecutor = executor;
152     }
153 
154     private class SelectorWorker implements Runnable {
155                 
156         public void run() {            
157             
158             String origName = Thread.currentThread().getName();
159             try {
160                Thread.currentThread().setName("Selector Worker: "+getId());
161                while ( isRunning() ) {
162 
163                    int count = selector.select(10);
164                    if (count == 0)
165                        continue;                
166                     if( !isRunning() )
167                         return;
168 
169                     // Get a java.util.Set containing the SelectionKey objects
170                     // for all channels that are ready for I/O.
171                     Set keys = selector.selectedKeys();
172     
173                     for (Iterator i = keys.iterator(); i.hasNext();) {                        
174                         final SelectionKey key = (SelectionKey) i.next();
175                         i.remove();
176 
177                         if( !key.isValid() ) 
178                             continue;
179                         
180                         final SocketChannelAsynchChannelSelection s = (SocketChannelAsynchChannelSelection) key.attachment();
181                         s.disable();
182                         
183                         // Kick off another thread to find newly selected keys while we process the 
184                         // currently selected keys                
185                         channelExecutor.execute(new Runnable() {
186                             public void run() {
187                                 try {
188                                     s.onSelect();
189                                     s.enable();
190                                 } catch ( Throwable e ) {
191                                     System.err.println("ActiveIO unexpected error: ");
192                                     e.printStackTrace(System.err);
193                                 }
194                             }
195                         });
196                     }
197                     
198                }
199             } catch (Throwable e) {
200                 System.err.println("Unexpected exception: " + e);
201                 e.printStackTrace();
202             } finally {
203                 Thread.currentThread().setName(origName);
204             }
205         }
206     }
207 
208     /***
209      * The selector used to wait for non-blocking events.
210      */
211     private Selector selector;
212 
213     /***
214      * How many SelectionKeys does the selector have active.
215      */
216     private int useCounter;
217     private int id = getNextId();
218     private static int nextId;
219 
220     private NIOAsynchChannelSelectorManager() throws IOException {
221         selector = Selector.open();
222     }
223     
224     synchronized private static int getNextId() {
225         return nextId++;
226     }
227 
228     private int getId() {
229         return id ;
230     }
231 
232     synchronized private void incrementUseCounter() {
233         useCounter++;
234         if (useCounter == 1) {
235             try {
236                 selectorExecutor.execute(new SelectorWorker());
237             } catch (InterruptedException e) {
238                 Thread.currentThread().interrupt();
239             }
240         }
241     }
242 
243     synchronized private void decrementUseCounter() {
244         useCounter--;
245 	 	synchronized(freeManagers) {	   	 		 
246  	 		 if( useCounter == 0 ) {
247   	 		 	freeManagers.remove(this);
248   	 		 }    	 		 
249  	 		 else if( useCounter < MAX_CHANNELS_PER_SELECTOR ) {
250   	 		 	fullManagers.remove(this);
251   	 		 	freeManagers.addLast(this);
252   	 		 }     	 		 
253 	    }
254     }
255 
256     synchronized private boolean isRunning() {
257         return useCounter > 0;
258     }
259 }