mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			400 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			400 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Java
		
	
	
	
| /* EpollSelectorImpl.java -- selector implementation using epoll
 | |
|    Copyright (C) 2006 Free Software Foundation, Inc.
 | |
| 
 | |
| This file is part of GNU Classpath.
 | |
| 
 | |
| GNU Classpath is free software; you can redistribute it and/or modify
 | |
| it under the terms of the GNU General Public License as published by
 | |
| the Free Software Foundation; either version 2, or (at your option)
 | |
| any later version.
 | |
| 
 | |
| GNU Classpath is distributed in the hope that it will be useful, but
 | |
| WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
| General Public License for more details.
 | |
| 
 | |
| You should have received a copy of the GNU General Public License
 | |
| along with GNU Classpath; see the file COPYING.  If not, write to the
 | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | |
| 02110-1301 USA.
 | |
| 
 | |
| Linking this library statically or dynamically with other modules is
 | |
| making a combined work based on this library.  Thus, the terms and
 | |
| conditions of the GNU General Public License cover the whole
 | |
| combination.
 | |
| 
 | |
| As a special exception, the copyright holders of this library give you
 | |
| permission to link this library with independent modules to produce an
 | |
| executable, regardless of the license terms of these independent
 | |
| modules, and to copy and distribute the resulting executable under
 | |
| terms of your choice, provided that you also meet, for each linked
 | |
| independent module, the terms and conditions of the license of that
 | |
| module.  An independent module is a module which is not derived from
 | |
| or based on this library.  If you modify this library, you may extend
 | |
| this exception to your version of the library, but you are not
 | |
| obligated to do so.  If you do not wish to do so, delete this
 | |
| exception statement from your version. */
 | |
| 
 | |
| 
 | |
| package gnu.java.nio;
 | |
| 
 | |
| import gnu.classpath.Configuration;
 | |
| 
 | |
| import java.io.IOException;
 | |
| import java.nio.ByteBuffer;
 | |
| import java.nio.channels.SelectableChannel;
 | |
| import java.nio.channels.SelectionKey;
 | |
| import java.nio.channels.Selector;
 | |
| import java.nio.channels.spi.AbstractSelectableChannel;
 | |
| import java.nio.channels.spi.AbstractSelector;
 | |
| import java.nio.channels.spi.SelectorProvider;
 | |
| import java.util.Collections;
 | |
| import java.util.HashMap;
 | |
| import java.util.HashSet;
 | |
| import java.util.Iterator;
 | |
| import java.util.Set;
 | |
| 
 | |
| /**
 | |
|  * An implementation of {@link Selector} that uses the epoll event
 | |
|  * notification mechanism on GNU/Linux.
 | |
|  *
 | |
|  * @author Casey Marshall (csm@gnu.org)
 | |
|  */
 | |
| public class EpollSelectorImpl extends AbstractSelector
 | |
| {
 | |
|   // XXX is this reasonable? Does it matter?
 | |
|   private static final int DEFAULT_EPOLL_SIZE = 128;
 | |
|   private static final int sizeof_struct_epoll_event;
 | |
| 
 | |
|   private static final int OP_ACCEPT  = SelectionKey.OP_ACCEPT;
 | |
|   private static final int OP_CONNECT = SelectionKey.OP_CONNECT;
 | |
|   private static final int OP_READ    = SelectionKey.OP_READ;
 | |
|   private static final int OP_WRITE   = SelectionKey.OP_WRITE;
 | |
| 
 | |
|   /** our epoll file descriptor. */
 | |
|   private int epoll_fd;
 | |
| 
 | |
|   private final HashMap keys;
 | |
|   private Set selectedKeys;
 | |
|   private Thread waitingThread;
 | |
|   private ByteBuffer events;
 | |
| 
 | |
|   private static final int INITIAL_CAPACITY;
 | |
|   private static final int MAX_DOUBLING_CAPACITY;
 | |
|   private static final int CAPACITY_INCREMENT;
 | |
| 
 | |
|   static
 | |
|   {
 | |
|     if (Configuration.INIT_LOAD_LIBRARY)
 | |
|       System.loadLibrary("javanio");
 | |
| 
 | |
|     if (epoll_supported())
 | |
|       sizeof_struct_epoll_event = sizeof_struct();
 | |
|     else
 | |
|       sizeof_struct_epoll_event = -1;
 | |
| 
 | |
|     INITIAL_CAPACITY = 64 * sizeof_struct_epoll_event;
 | |
|     MAX_DOUBLING_CAPACITY = 1024 * sizeof_struct_epoll_event;
 | |
|     CAPACITY_INCREMENT = 128 * sizeof_struct_epoll_event;
 | |
|   }
 | |
| 
 | |
|   public EpollSelectorImpl(SelectorProvider provider)
 | |
|     throws IOException
 | |
|   {
 | |
|     super(provider);
 | |
|     epoll_fd = epoll_create(DEFAULT_EPOLL_SIZE);
 | |
|     keys = new HashMap();
 | |
|     selectedKeys = null;
 | |
|     events = ByteBuffer.allocateDirect(INITIAL_CAPACITY);
 | |
|   }
 | |
| 
 | |
|   /* (non-Javadoc)
 | |
|    * @see java.nio.channels.Selector#keys()
 | |
|    */
 | |
|   public Set keys()
 | |
|   {
 | |
|     return new HashSet(keys.values());
 | |
|   }
 | |
| 
 | |
|   /* (non-Javadoc)
 | |
|    * @see java.nio.channels.Selector#select()
 | |
|    */
 | |
|   public int select() throws IOException
 | |
|   {
 | |
|     return doSelect(-1);
 | |
|   }
 | |
| 
 | |
|   /* (non-Javadoc)
 | |
|    * @see java.nio.channels.Selector#select(long)
 | |
|    */
 | |
|   public int select(long timeout) throws IOException
 | |
|   {
 | |
|     if (timeout > Integer.MAX_VALUE)
 | |
|       throw new IllegalArgumentException("timeout is too large");
 | |
|     if (timeout < 0)
 | |
|       throw new IllegalArgumentException("invalid timeout");
 | |
|     return doSelect((int) timeout);
 | |
|   }
 | |
| 
 | |
|   private int doSelect(int timeout) throws IOException
 | |
|   {
 | |
|     synchronized (keys)
 | |
|     {
 | |
|       Set cancelledKeys = cancelledKeys();
 | |
|       synchronized (cancelledKeys)
 | |
|       {
 | |
|         for (Iterator it = cancelledKeys.iterator(); it.hasNext(); )
 | |
|           {
 | |
|             EpollSelectionKeyImpl key = (EpollSelectionKeyImpl) it.next();
 | |
|             epoll_delete(epoll_fd, key.fd);
 | |
|             key.valid = false;
 | |
|             keys.remove(Integer.valueOf(key.fd));
 | |
|             it.remove();
 | |
|             deregister(key);
 | |
|           }
 | |
| 
 | |
|         // Clear out closed channels. The fds are removed from the epoll
 | |
|         // fd when closed, so there is no need to remove them manually.
 | |
|         for (Iterator it = keys.values().iterator(); it.hasNext(); )
 | |
|           {
 | |
|             EpollSelectionKeyImpl key = (EpollSelectionKeyImpl) it.next();
 | |
|             SelectableChannel ch = key.channel();
 | |
|             if (ch instanceof VMChannelOwner)
 | |
|               {
 | |
|                 if (!((VMChannelOwner) ch).getVMChannel().getState().isValid())
 | |
|                   it.remove();
 | |
|               }
 | |
|           }
 | |
| 
 | |
|         // Don't bother if we have nothing to select.
 | |
|         if (keys.isEmpty())
 | |
|           return 0;
 | |
| 
 | |
|         int ret;
 | |
|         try
 | |
|           {
 | |
|             begin();
 | |
|             waitingThread = Thread.currentThread();
 | |
|             ret = epoll_wait(epoll_fd, events, keys.size(), timeout);
 | |
|           }
 | |
|         finally
 | |
|           {
 | |
|             Thread.interrupted();
 | |
|             waitingThread = null;
 | |
|             end();
 | |
|           }
 | |
| 
 | |
|         HashSet s = new HashSet(ret);
 | |
|         for (int i = 0; i < ret; i++)
 | |
|           {
 | |
|             events.position(i * sizeof_struct_epoll_event);
 | |
|             ByteBuffer b = events.slice();
 | |
|             int fd = selected_fd(b);
 | |
|             EpollSelectionKeyImpl key
 | |
|               = (EpollSelectionKeyImpl) keys.get(Integer.valueOf(fd));
 | |
|             if (key == null)
 | |
|               throw new IOException("fd was selected, but no key found");
 | |
|             key.selectedOps = selected_ops(b) & key.interestOps;
 | |
|             s.add(key);
 | |
|           }
 | |
| 
 | |
|         reallocateBuffer();
 | |
| 
 | |
|         selectedKeys = s;
 | |
|         return ret;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* (non-Javadoc)
 | |
|    * @see java.nio.channels.Selector#selectedKeys()
 | |
|    */
 | |
|   public Set selectedKeys()
 | |
|   {
 | |
|     if (selectedKeys == null)
 | |
|       return Collections.EMPTY_SET;
 | |
|     return selectedKeys;
 | |
|   }
 | |
| 
 | |
|   /* (non-Javadoc)
 | |
|    * @see java.nio.channels.Selector#selectNow()
 | |
|    */
 | |
|   public int selectNow() throws IOException
 | |
|   {
 | |
|     return doSelect(0);
 | |
|   }
 | |
| 
 | |
|   /* (non-Javadoc)
 | |
|    * @see java.nio.channels.Selector#wakeup()
 | |
|    */
 | |
|   public Selector wakeup()
 | |
|   {
 | |
|     try
 | |
|       {
 | |
|         waitingThread.interrupt();
 | |
|       }
 | |
|     catch (NullPointerException npe)
 | |
|       {
 | |
|         // Ignored, thrown if we are not in a blocking op.
 | |
|       }
 | |
|     return this;
 | |
|   }
 | |
| 
 | |
|   /* (non-Javadoc)
 | |
|    * @see java.nio.channels.spi.AbstractSelector#implCloseSelector()
 | |
|    */
 | |
|   protected void implCloseSelector() throws IOException
 | |
|   {
 | |
|     VMChannel.close(epoll_fd);
 | |
|   }
 | |
| 
 | |
|   /* (non-Javadoc)
 | |
|    * @see java.nio.channels.spi.AbstractSelector#register(java.nio.channels.spi.AbstractSelectableChannel, int, java.lang.Object)
 | |
|    */
 | |
|   protected SelectionKey register(AbstractSelectableChannel ch, int ops, Object att)
 | |
|   {
 | |
|     if (!(ch instanceof VMChannelOwner))
 | |
|       throw new IllegalArgumentException("unsupported channel type");
 | |
| 
 | |
|     VMChannel channel = ((VMChannelOwner) ch).getVMChannel();
 | |
|     try
 | |
|       {
 | |
|         int native_fd = channel.getState().getNativeFD();
 | |
|         synchronized (keys)
 | |
|         {
 | |
|           if (keys.containsKey(Integer.valueOf(native_fd)))
 | |
|             throw new IllegalArgumentException("channel already registered");
 | |
|           EpollSelectionKeyImpl result =
 | |
|             new EpollSelectionKeyImpl(this, ch, native_fd);
 | |
|           if ((ops & ~(ch.validOps())) != 0)
 | |
|             throw new IllegalArgumentException("invalid ops for channel");
 | |
|           result.interestOps = ops;
 | |
|           result.selectedOps = 0;
 | |
|           result.valid = true;
 | |
|           result.attach(att);
 | |
|           result.key = System.identityHashCode(result);
 | |
|           epoll_add(epoll_fd, result.fd, ops);
 | |
|           keys.put(Integer.valueOf(native_fd), result);
 | |
|           reallocateBuffer();
 | |
|           return result;
 | |
|         }
 | |
|       }
 | |
|     catch (IOException ioe)
 | |
|       {
 | |
|         throw new IllegalArgumentException(ioe);
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   private void reallocateBuffer()
 | |
|   {
 | |
|     // Ensure we have enough space for all potential events that may be
 | |
|     // returned.
 | |
|     if (events.capacity() < keys.size() * sizeof_struct_epoll_event)
 | |
|       {
 | |
|         int cap = events.capacity();
 | |
|         if (cap < MAX_DOUBLING_CAPACITY)
 | |
|           cap <<= 1;
 | |
|         else
 | |
|           cap += CAPACITY_INCREMENT;
 | |
|         events = ByteBuffer.allocateDirect(cap);
 | |
|       }
 | |
|     // Ensure that the events buffer is not too large, given the number of
 | |
|     // events registered.
 | |
|     else if (events.capacity() > keys.size() * sizeof_struct_epoll_event * 2 + 1
 | |
|              && events.capacity() > INITIAL_CAPACITY)
 | |
|       {
 | |
|         int cap = events.capacity() >>> 1;
 | |
|         events = ByteBuffer.allocateDirect(cap);
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   void epoll_modify(EpollSelectionKeyImpl key, int ops) throws IOException
 | |
|   {
 | |
|     epoll_modify(epoll_fd, key.fd, ops);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Tell if epoll is supported by this system, and support was compiled in.
 | |
|    *
 | |
|    * @return True if this system supports event notification with epoll.
 | |
|    */
 | |
|   public static native boolean epoll_supported();
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * Returns the size of `struct epoll_event'.
 | |
|    *
 | |
|    * @return The size of `struct epoll_event'.
 | |
|    */
 | |
|   private static native int sizeof_struct();
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * Open a new epoll file descriptor.
 | |
|    *
 | |
|    * @param size The size hint for the new epoll descriptor.
 | |
|    * @return The new file descriptor integer.
 | |
|    * @throws IOException If allocating a new epoll descriptor fails.
 | |
|    */
 | |
|   private static native int epoll_create(int size) throws IOException;
 | |
| 
 | |
|   /**
 | |
|    * Add a file descriptor to this selector.
 | |
|    *
 | |
|    * @param efd The epoll file descriptor.
 | |
|    * @param fd  The file descriptor to add (or modify).
 | |
|    * @param ops The interest opts.
 | |
|    */
 | |
|   private static native void epoll_add(int efd, int fd, int ops)
 | |
|     throws IOException;
 | |
| 
 | |
|   /**
 | |
|    * Modify the interest ops of the key selecting for the given FD.
 | |
|    *
 | |
|    * @param efd The epoll file descriptor.
 | |
|    * @param fd  The file descriptor to modify.
 | |
|    * @param ops The ops.
 | |
|    * @throws IOException
 | |
|    */
 | |
|   private static native void epoll_modify(int efd, int fd, int ops)
 | |
|     throws IOException;
 | |
| 
 | |
|   /**
 | |
|    * Remove a file descriptor from this selector.
 | |
|    *
 | |
|    * @param efd The epoll file descriptor.
 | |
|    * @param fd  The file descriptor.
 | |
|    * @throws IOException
 | |
|    */
 | |
|   private static native void epoll_delete(int efd, int fd) throws IOException;
 | |
| 
 | |
|   /**
 | |
|    * Select events.
 | |
|    *
 | |
|    * @param efd     The epoll file descriptor.
 | |
|    * @param state   The buffer to hold selected events.
 | |
|    * @param n       The number of events that may be put in `state'.
 | |
|    * @param timeout The timeout.
 | |
|    * @return The number of events selected.
 | |
|    * @throws IOException
 | |
|    */
 | |
|   private static native int epoll_wait(int efd, ByteBuffer state, int n, int timeout)
 | |
|     throws IOException;
 | |
| 
 | |
|   /**
 | |
|    * Fetch the fd value from a selected struct epoll_event.
 | |
|    *
 | |
|    * @param struct The direct buffer holding the struct.
 | |
|    * @return The fd value.
 | |
|    */
 | |
|   private static native int selected_fd(ByteBuffer struct);
 | |
| 
 | |
|   /**
 | |
|    * Fetch the enabled operations from a selected struct epoll_event.
 | |
|    *
 | |
|    * @param struct The direct buffer holding the struct.
 | |
|    * @return The selected operations.
 | |
|    */
 | |
|   private static native int selected_ops(ByteBuffer struct);
 | |
| }
 |