/* * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Sun Microsystems nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ import java.lang.reflect.*; import java.io.*; import java.net.*; /** * This class is provided for access to the underlying poll(2) * or /dev/poll kernel interfaces. This may be needed for * multiplexing IO when an application cannot afford to have * a thread block on each outstanding IO request. * * It currently supports the same basic functionality as the * C poll(2) API, although for efficiency we needed to avoid * passing the entire pollfd array for every call. See man * pages for poll(2) for info on C API and event types. * * * @author Bruce Chapman * @see java.io.FileDescriptor * @see java.net.Socket * @see attached README.txt * @since JDK1.2 */ public class Poller { /** * Solaris POLL event types. */ public final static short POLLERR = 0x08; public final static short POLLHUP = 0x10; public final static short POLLNVAL = 0x20; public final static short POLLIN = 1; public final static short POLLPRI = 2; public final static short POLLOUT = 4; public final static short POLLRDNORM = 0x40; public final static short POLLWRNORM = POLLOUT ; public final static short POLLRDBAND = 0x80; public final static short POLLWRBAND = 0x100; public final static short POLLNORM = POLLRDNORM; /* * This global synchronization object must be used for all * creation or destruction of Poller objects. */ private final static Object globalSync = new Object(); /* * The handle for a Poller Object...is used in the JNI C code * where all the associated data is kept. */ private int handle; /** * Constructs an instance of a <code>Poller</code> object. * Native code uses sysconf(_SC_OPEN_MAX) to determine how * many fd/skt objects this Poller object can contain. */ public Poller() throws Exception { synchronized(globalSync) { this.handle = nativeCreatePoller(-1); } } /** * Constructs an instance of a <code>Poller</code> object. * @param maxFd the maximum number of FileDescriptors/Sockets * this Poller object can contain. */ public Poller(int maxFd) throws Exception { synchronized(globalSync) { this.handle = nativeCreatePoller(maxFd); } } /** * Needed to clean up at the JNI C level when object is GCd. */ protected void finalize() throws Throwable { synchronized(globalSync) { nativeDestroyPoller(handle); super.finalize(); } } /** * Since we can't guarantee WHEN finalize is called, we may * recycle on our own. * @param maxFd the maximum number of FileDescriptors/Sockets * this Poller object can contain. */ public void reset(int maxFd) throws Exception { synchronized(globalSync) { nativeDestroyPoller(handle); this.handle = nativeCreatePoller(maxFd); } } /** * Since we can't guarantee WHEN finalize is called, we may * recycle on our own. */ public void reset() throws Exception { synchronized(globalSync) { nativeDestroyPoller(handle); this.handle = nativeCreatePoller(-1); } } /** * Add FileDescriptor to the set handled by this Poller object. * * @param fdObj the FileDescriptor, Socket, or ServerSocket to add. * @param event the bitmask of events we are interested in. * @return the OS level fd associated with this IO Object * (which is what waitMultiple() stores in fds[]) */ public synchronized int add(Object fdObj, short event) throws Exception { return nativeAddFd(handle,findfd(fdObj), event); } /** * Remove FileDescriptor from the set handled by this Poller object. * * Must be called before the fd/skt is closed. * @param fdObj the FileDescriptor, Socket, or ServerSocket to remove. * @return true if removal succeeded. */ public synchronized boolean remove(Object fdObj) throws Exception { return (nativeRemoveFd(handle,findfd(fdObj)) == 1); } /** * Check if fd or socket is already in the set handled by this Poller object * * @param fdObj the FileDescriptor or [Server]Socket to check. * @return true if fd/skt is in the set for this Poller object. */ public synchronized boolean isMember(Object fdObj) throws Exception { return (nativeIsMember(handle,findfd(fdObj)) == 1); } /** * Wait on Multiple IO Objects. * * @param maxRet the maximum number of fds[] and revents[] to return. * @param fds[] (return) an array of ints in which to store fds with * available data upon a successful non-timeout return. * fds.length must be >= maxRet * @param revents[] (return) the actual events available on the * same-indexed fds[] (i.e. fds[0] has events revents[0]) * revents.length must be >= maxRet * * Note : both above arrays are "dense," i.e. only fds[] with events * available are returned. * * @param timeout the maximum number of milliseconds to wait for * events before timing out. * @return the number of fds with triggered events. * * Note : convenience methods exist for skipping the timeout parameter * or the maxRet parameter (in the case of no maxRet, fds.length * must equal revents.length) * * obj.waitMultiple(null,null,timeout) can be used for pausing the LWP * (much more reliable and scalable than Thread.sleep() or Object.wait()) */ public synchronized int waitMultiple(int maxRet, int[] fds,short[] revents, long timeout) throws Exception { if ((revents == null) || (fds == null)) { if (maxRet > 0) { throw new NullPointerException("fds or revents is null"); } } else if ( (maxRet < 0) || (maxRet > revents.length) || (maxRet > fds.length) ) { throw new IllegalArgumentException("maxRet out of range"); } int ret = nativeWait(handle, maxRet, fds, revents, timeout); if (ret < 0) { throw new InterruptedIOException(); } return ret; } /** * Wait on Multiple IO Objects (no timeout). * A convenience method for waiting indefinitely on IO events * * @see Poller#waitMultiple * */ public int waitMultiple(int maxRet, int[] fds, short[] revents) throws Exception { return waitMultiple(maxRet, fds, revents,-1L); // already synchronized } /** * Wait on Multiple IO Objects (no maxRet). * A convenience method for waiting on IO events when the fds * and revents arrays are the same length and that specifies the * maximum number of return events. * * @see Poller#waitMultiple * */ public synchronized int waitMultiple(int[] fds, short[] revents, long timeout) throws Exception { if ((revents == null) && (fds == null)) { return nativeWait(handle,0,null,null,timeout); } else if ((revents == null) || (fds == null)) { throw new NullPointerException("revents or fds is null"); } else if (fds.length == revents.length) { return nativeWait(handle, fds.length, fds, revents, timeout); } throw new IllegalArgumentException("fds.length != revents.length"); } /** * Wait on Multiple IO Objects (no maxRet/timeout). * A convenience method for waiting on IO events when the fds * and revents arrays are the same length and that specifies the * maximum number of return events, and when waiting indefinitely * for IO events to occur. * * @see Poller#waitMultiple * */ public int waitMultiple(int[] fds, short[] revents) throws Exception { if ((revents == null) || (fds == null)) { throw new NullPointerException("fds or revents is null"); } else if (fds.length == revents.length) { return waitMultiple(revents.length,fds,revents,-1L); // already sync } throw new IllegalArgumentException("fds.length != revents.length"); } // Utility - get (int) fd from FileDescriptor or [Server]Socket objects. private int findfd(Object fdObj) throws Exception { Class cl; Field f; Object val, implVal; if ((fdObj instanceof java.net.Socket) || (fdObj instanceof java.net.ServerSocket)) { cl = fdObj.getClass(); f = cl.getDeclaredField("impl"); f.setAccessible(true); val = f.get(fdObj); cl = f.getType(); f = cl.getDeclaredField("fd"); f.setAccessible(true); implVal = f.get(val); cl = f.getType(); f = cl.getDeclaredField("fd"); f.setAccessible(true); return ((Integer) f.get(implVal)).intValue(); } else if ( fdObj instanceof java.io.FileDescriptor ) { cl = fdObj.getClass(); f = cl.getDeclaredField("fd"); f.setAccessible(true); return ((Integer) f.get(fdObj)).intValue(); } else { throw new IllegalArgumentException("Illegal Object type."); } } // Actual NATIVE calls private static native int nativeInit(); private native int nativeCreatePoller(int maxFd) throws Exception; private native void nativeDestroyPoller(int handle) throws Exception; private native int nativeAddFd(int handle, int fd, short events) throws Exception; private native int nativeRemoveFd(int handle, int fd) throws Exception; private native int nativeRemoveIndex(int handle, int index) throws Exception; private native int nativeIsMember(int handle, int fd) throws Exception; private native int nativeWait(int handle, int maxRet, int[] fds, short[] events, long timeout) throws Exception; /** * Get number of active CPUs in this machine * to determine proper level of concurrency. */ public static native int getNumCPUs(); static { System.loadLibrary("poller"); nativeInit(); } }