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);
 | 
						|
}
 |