1 /* EpollSelectorImpl.java -- selector implementation using epoll
2 Copyright (C) 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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)
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.
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
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
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. */
41 import gnu.classpath.Configuration;
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;
58 * An implementation of {@link Selector} that uses the epoll event
59 * notification mechanism on GNU/Linux.
61 * @author Casey Marshall (csm@gnu.org)
63 public class EpollSelectorImpl extends AbstractSelector
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;
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;
74 /** our epoll file descriptor. */
77 private final HashMap keys;
78 private Set selectedKeys;
79 private Thread waitingThread;
80 private ByteBuffer events;
82 private static final int INITIAL_CAPACITY;
83 private static final int MAX_DOUBLING_CAPACITY;
84 private static final int CAPACITY_INCREMENT;
88 if (Configuration.INIT_LOAD_LIBRARY)
89 System.loadLibrary("javanio");
91 if (epoll_supported())
92 sizeof_struct_epoll_event = sizeof_struct();
94 sizeof_struct_epoll_event = -1;
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;
101 public EpollSelectorImpl(SelectorProvider provider)
105 epoll_fd = epoll_create(DEFAULT_EPOLL_SIZE);
106 keys = new HashMap();
108 events = ByteBuffer.allocateDirect(INITIAL_CAPACITY);
112 * @see java.nio.channels.Selector#keys()
116 return new HashSet(keys.values());
120 * @see java.nio.channels.Selector#select()
122 public int select() throws IOException
128 * @see java.nio.channels.Selector#select(long)
130 public int select(long timeout) throws IOException
132 if (timeout > Integer.MAX_VALUE)
133 throw new IllegalArgumentException("timeout is too large");
135 throw new IllegalArgumentException("invalid timeout");
136 return doSelect((int) timeout);
139 private int doSelect(int timeout) throws IOException
143 Set cancelledKeys = cancelledKeys();
144 synchronized (cancelledKeys)
146 for (Iterator it = cancelledKeys.iterator(); it.hasNext(); )
148 EpollSelectionKeyImpl key = (EpollSelectionKeyImpl) it.next();
149 epoll_delete(epoll_fd, key.fd);
151 keys.remove(Integer.valueOf(key.fd));
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(); )
160 EpollSelectionKeyImpl key = (EpollSelectionKeyImpl) it.next();
161 SelectableChannel ch = key.channel();
162 if (ch instanceof VMChannelOwner)
164 if (!((VMChannelOwner) ch).getVMChannel().getState().isValid())
169 // Don't bother if we have nothing to select.
177 waitingThread = Thread.currentThread();
178 ret = epoll_wait(epoll_fd, events, keys.size(), timeout);
182 Thread.interrupted();
183 waitingThread = null;
187 HashSet s = new HashSet(ret);
188 for (int i = 0; i < ret; i++)
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));
196 throw new IOException("fd was selected, but no key found");
197 key.selectedOps = selected_ops(b) & key.interestOps;
210 * @see java.nio.channels.Selector#selectedKeys()
212 public Set selectedKeys()
214 if (selectedKeys == null)
215 return Collections.EMPTY_SET;
220 * @see java.nio.channels.Selector#selectNow()
222 public int selectNow() throws IOException
228 * @see java.nio.channels.Selector#wakeup()
230 public Selector wakeup()
234 waitingThread.interrupt();
236 catch (NullPointerException npe)
238 // Ignored, thrown if we are not in a blocking op.
244 * @see java.nio.channels.spi.AbstractSelector#implCloseSelector()
246 protected void implCloseSelector() throws IOException
248 VMChannel.close(epoll_fd);
252 * @see java.nio.channels.spi.AbstractSelector#register(java.nio.channels.spi.AbstractSelectableChannel, int, java.lang.Object)
254 protected SelectionKey register(AbstractSelectableChannel ch, int ops, Object att)
256 if (!(ch instanceof VMChannelOwner))
257 throw new IllegalArgumentException("unsupported channel type");
259 VMChannel channel = ((VMChannelOwner) ch).getVMChannel();
262 int native_fd = channel.getState().getNativeFD();
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;
275 result.key = System.identityHashCode(result);
276 epoll_add(epoll_fd, result.fd, ops);
277 keys.put(Integer.valueOf(native_fd), result);
282 catch (IOException ioe)
284 throw new IllegalArgumentException(ioe);
288 private void reallocateBuffer()
290 // Ensure we have enough space for all potential events that may be
292 if (events.capacity() < keys.size() * sizeof_struct_epoll_event)
294 int cap = events.capacity();
295 if (cap < MAX_DOUBLING_CAPACITY)
298 cap += CAPACITY_INCREMENT;
299 events = ByteBuffer.allocateDirect(cap);
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)
306 int cap = events.capacity() >>> 1;
307 events = ByteBuffer.allocateDirect(cap);
311 void epoll_modify(EpollSelectionKeyImpl key, int ops) throws IOException
313 epoll_modify(epoll_fd, key.fd, ops);
317 * Tell if epoll is supported by this system, and support was compiled in.
319 * @return True if this system supports event notification with epoll.
321 public static native boolean epoll_supported();
325 * Returns the size of `struct epoll_event'.
327 * @return The size of `struct epoll_event'.
329 private static native int sizeof_struct();
333 * Open a new epoll file descriptor.
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.
339 private static native int epoll_create(int size) throws IOException;
342 * Add a file descriptor to this selector.
344 * @param efd The epoll file descriptor.
345 * @param fd The file descriptor to add (or modify).
346 * @param ops The interest opts.
348 private static native void epoll_add(int efd, int fd, int ops)
352 * Modify the interest ops of the key selecting for the given FD.
354 * @param efd The epoll file descriptor.
355 * @param fd The file descriptor to modify.
356 * @param ops The ops.
357 * @throws IOException
359 private static native void epoll_modify(int efd, int fd, int ops)
363 * Remove a file descriptor from this selector.
365 * @param efd The epoll file descriptor.
366 * @param fd The file descriptor.
367 * @throws IOException
369 private static native void epoll_delete(int efd, int fd) throws IOException;
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
381 private static native int epoll_wait(int efd, ByteBuffer state, int n, int timeout)
385 * Fetch the fd value from a selected struct epoll_event.
387 * @param struct The direct buffer holding the struct.
388 * @return The fd value.
390 private static native int selected_fd(ByteBuffer struct);
393 * Fetch the enabled operations from a selected struct epoll_event.
395 * @param struct The direct buffer holding the struct.
396 * @return The selected operations.
398 private static native int selected_ops(ByteBuffer struct);