OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / java / nio / EpollSelectorImpl.java
1 /* EpollSelectorImpl.java -- selector implementation using epoll
2    Copyright (C) 2006 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package gnu.java.nio;
40
41 import gnu.classpath.Configuration;
42
43 import java.io.IOException;
44 import java.nio.ByteBuffer;
45 import java.nio.channels.SelectableChannel;
46 import java.nio.channels.SelectionKey;
47 import java.nio.channels.Selector;
48 import java.nio.channels.spi.AbstractSelectableChannel;
49 import java.nio.channels.spi.AbstractSelector;
50 import java.nio.channels.spi.SelectorProvider;
51 import java.util.Collections;
52 import java.util.HashMap;
53 import java.util.HashSet;
54 import java.util.Iterator;
55 import java.util.Set;
56
57 /**
58  * An implementation of {@link Selector} that uses the epoll event
59  * notification mechanism on GNU/Linux.
60  *
61  * @author Casey Marshall (csm@gnu.org)
62  */
63 public class EpollSelectorImpl extends AbstractSelector
64 {
65   // XXX is this reasonable? Does it matter?
66   private static final int DEFAULT_EPOLL_SIZE = 128;
67   private static final int sizeof_struct_epoll_event;
68   
69   private static final int OP_ACCEPT  = SelectionKey.OP_ACCEPT;
70   private static final int OP_CONNECT = SelectionKey.OP_CONNECT;
71   private static final int OP_READ    = SelectionKey.OP_READ;
72   private static final int OP_WRITE   = SelectionKey.OP_WRITE;
73   
74   /** our epoll file descriptor. */
75   private int epoll_fd;
76   
77   private final HashMap keys;
78   private Set selectedKeys;
79   private Thread waitingThread;
80   private ByteBuffer events;
81   
82   private static final int INITIAL_CAPACITY;
83   private static final int MAX_DOUBLING_CAPACITY;
84   private static final int CAPACITY_INCREMENT;
85
86   static
87   {
88     if (Configuration.INIT_LOAD_LIBRARY)
89       System.loadLibrary("javanio");
90     
91     if (epoll_supported())
92       sizeof_struct_epoll_event = sizeof_struct();
93     else
94       sizeof_struct_epoll_event = -1;
95     
96     INITIAL_CAPACITY = 64 * sizeof_struct_epoll_event;
97     MAX_DOUBLING_CAPACITY = 1024 * sizeof_struct_epoll_event;
98     CAPACITY_INCREMENT = 128 * sizeof_struct_epoll_event;
99   }
100   
101   public EpollSelectorImpl(SelectorProvider provider)
102     throws IOException
103   {
104     super(provider);
105     epoll_fd = epoll_create(DEFAULT_EPOLL_SIZE);
106     keys = new HashMap();
107     selectedKeys = null;
108     events = ByteBuffer.allocateDirect(INITIAL_CAPACITY);
109   }
110
111   /* (non-Javadoc)
112    * @see java.nio.channels.Selector#keys()
113    */
114   public Set keys()
115   {
116     return new HashSet(keys.values());
117   }
118
119   /* (non-Javadoc)
120    * @see java.nio.channels.Selector#select()
121    */
122   public int select() throws IOException
123   {
124     return doSelect(-1);
125   }
126
127   /* (non-Javadoc)
128    * @see java.nio.channels.Selector#select(long)
129    */
130   public int select(long timeout) throws IOException
131   {
132     if (timeout > Integer.MAX_VALUE)
133       throw new IllegalArgumentException("timeout is too large");
134     if (timeout < 0)
135       throw new IllegalArgumentException("invalid timeout");
136     return doSelect((int) timeout);
137   }
138     
139   private int doSelect(int timeout) throws IOException
140   {
141     synchronized (keys)
142     {
143       Set cancelledKeys = cancelledKeys();
144       synchronized (cancelledKeys)
145       {
146         for (Iterator it = cancelledKeys.iterator(); it.hasNext(); )
147           {
148             EpollSelectionKeyImpl key = (EpollSelectionKeyImpl) it.next();
149             epoll_delete(epoll_fd, key.fd);
150             key.valid = false;
151             keys.remove(Integer.valueOf(key.fd));
152             it.remove();
153             deregister(key);
154           }
155         
156         // Clear out closed channels. The fds are removed from the epoll
157         // fd when closed, so there is no need to remove them manually.
158         for (Iterator it = keys.values().iterator(); it.hasNext(); )
159           {
160             EpollSelectionKeyImpl key = (EpollSelectionKeyImpl) it.next();
161             SelectableChannel ch = key.channel();
162             if (ch instanceof VMChannelOwner)
163               {
164                 if (!((VMChannelOwner) ch).getVMChannel().getState().isValid())
165                   it.remove();
166               }
167           }
168         
169         // Don't bother if we have nothing to select.
170         if (keys.isEmpty())
171           return 0;
172
173         int ret;
174         try
175           {
176             begin();
177             waitingThread = Thread.currentThread();
178             ret = epoll_wait(epoll_fd, events, keys.size(), timeout);
179           }
180         finally
181           {
182             Thread.interrupted();
183             waitingThread = null;
184             end();
185           }
186       
187         HashSet s = new HashSet(ret);
188         for (int i = 0; i < ret; i++)
189           {
190             events.position(i * sizeof_struct_epoll_event);
191             ByteBuffer b = events.slice();
192             int fd = selected_fd(b);
193             EpollSelectionKeyImpl key
194               = (EpollSelectionKeyImpl) keys.get(Integer.valueOf(fd));
195             if (key == null)
196               throw new IOException("fd was selected, but no key found");
197             key.selectedOps = selected_ops(b) & key.interestOps;
198             s.add(key);
199           }
200         
201         reallocateBuffer();
202         
203         selectedKeys = s;
204         return ret;
205       }
206     }
207   }
208
209   /* (non-Javadoc)
210    * @see java.nio.channels.Selector#selectedKeys()
211    */
212   public Set selectedKeys()
213   {
214     if (selectedKeys == null)
215       return Collections.EMPTY_SET;
216     return selectedKeys;
217   }
218
219   /* (non-Javadoc)
220    * @see java.nio.channels.Selector#selectNow()
221    */
222   public int selectNow() throws IOException
223   {
224     return doSelect(0);
225   }
226
227   /* (non-Javadoc)
228    * @see java.nio.channels.Selector#wakeup()
229    */
230   public Selector wakeup()
231   {
232     try
233       {
234         waitingThread.interrupt();
235       }
236     catch (NullPointerException npe)
237       {
238         // Ignored, thrown if we are not in a blocking op.
239       }
240     return this;
241   }
242   
243   /* (non-Javadoc)
244    * @see java.nio.channels.spi.AbstractSelector#implCloseSelector()
245    */
246   protected void implCloseSelector() throws IOException
247   {
248     VMChannel.close(epoll_fd);
249   }
250
251   /* (non-Javadoc)
252    * @see java.nio.channels.spi.AbstractSelector#register(java.nio.channels.spi.AbstractSelectableChannel, int, java.lang.Object)
253    */
254   protected SelectionKey register(AbstractSelectableChannel ch, int ops, Object att)
255   {
256     if (!(ch instanceof VMChannelOwner))
257       throw new IllegalArgumentException("unsupported channel type");
258
259     VMChannel channel = ((VMChannelOwner) ch).getVMChannel();
260     try
261       {
262         int native_fd = channel.getState().getNativeFD();
263         synchronized (keys)
264         {
265           if (keys.containsKey(Integer.valueOf(native_fd)))
266             throw new IllegalArgumentException("channel already registered");
267           EpollSelectionKeyImpl result =
268             new EpollSelectionKeyImpl(this, ch, native_fd);
269           if ((ops & ~(ch.validOps())) != 0)
270             throw new IllegalArgumentException("invalid ops for channel");
271           result.interestOps = ops;
272           result.selectedOps = 0;
273           result.valid = true;
274           result.attach(att);
275           result.key = System.identityHashCode(result);
276           epoll_add(epoll_fd, result.fd, ops);
277           keys.put(Integer.valueOf(native_fd), result);
278           reallocateBuffer();
279           return result;
280         }
281       }
282     catch (IOException ioe)
283       {
284         throw new IllegalArgumentException(ioe);
285       }
286   }
287   
288   private void reallocateBuffer()
289   {
290     // Ensure we have enough space for all potential events that may be
291     // returned.
292     if (events.capacity() < keys.size() * sizeof_struct_epoll_event)
293       {
294         int cap = events.capacity();
295         if (cap < MAX_DOUBLING_CAPACITY)
296           cap <<= 1;
297         else
298           cap += CAPACITY_INCREMENT;
299         events = ByteBuffer.allocateDirect(cap);
300       }
301     // Ensure that the events buffer is not too large, given the number of
302     // events registered.
303     else if (events.capacity() > keys.size() * sizeof_struct_epoll_event * 2 + 1
304              && events.capacity() > INITIAL_CAPACITY)
305       {
306         int cap = events.capacity() >>> 1;
307         events = ByteBuffer.allocateDirect(cap);
308       }
309   }
310   
311   void epoll_modify(EpollSelectionKeyImpl key, int ops) throws IOException
312   {
313     epoll_modify(epoll_fd, key.fd, ops);
314   }
315   
316   /**
317    * Tell if epoll is supported by this system, and support was compiled in.
318    *
319    * @return True if this system supports event notification with epoll.
320    */
321   public static native boolean epoll_supported();
322
323
324   /**
325    * Returns the size of `struct epoll_event'.
326    *
327    * @return The size of `struct epoll_event'.
328    */
329   private static native int sizeof_struct();
330   
331  
332   /**
333    * Open a new epoll file descriptor.
334    *
335    * @param size The size hint for the new epoll descriptor.
336    * @return The new file descriptor integer.
337    * @throws IOException If allocating a new epoll descriptor fails.
338    */
339   private static native int epoll_create(int size) throws IOException;
340   
341   /**
342    * Add a file descriptor to this selector.
343    *
344    * @param efd The epoll file descriptor.
345    * @param fd  The file descriptor to add (or modify).
346    * @param ops The interest opts.
347    */
348   private static native void epoll_add(int efd, int fd, int ops)
349     throws IOException;
350   
351   /**
352    * Modify the interest ops of the key selecting for the given FD.
353    *
354    * @param efd The epoll file descriptor.
355    * @param fd  The file descriptor to modify.
356    * @param ops The ops.
357    * @throws IOException
358    */
359   private static native void epoll_modify(int efd, int fd, int ops)
360     throws IOException;
361   
362   /**
363    * Remove a file descriptor from this selector.
364    *
365    * @param efd The epoll file descriptor.
366    * @param fd  The file descriptor.
367    * @throws IOException
368    */
369   private static native void epoll_delete(int efd, int fd) throws IOException;
370   
371   /**
372    * Select events.
373    *
374    * @param efd     The epoll file descriptor.
375    * @param state   The buffer to hold selected events.
376    * @param n       The number of events that may be put in `state'.
377    * @param timeout The timeout.
378    * @return The number of events selected.
379    * @throws IOException
380    */
381   private static native int epoll_wait(int efd, ByteBuffer state, int n, int timeout)
382     throws IOException;
383   
384   /**
385    * Fetch the fd value from a selected struct epoll_event.
386    *
387    * @param struct The direct buffer holding the struct.
388    * @return The fd value.
389    */
390   private static native int selected_fd(ByteBuffer struct);
391   
392   /**
393    * Fetch the enabled operations from a selected struct epoll_event.
394    *
395    * @param struct The direct buffer holding the struct.
396    * @return The selected operations.
397    */
398   private static native int selected_ops(ByteBuffer struct);
399 }