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