J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved. |
| 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions |
| 6 | * are met: |
| 7 | * |
| 8 | * - Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * |
| 11 | * - Redistributions in binary form must reproduce the above copyright |
| 12 | * notice, this list of conditions and the following disclaimer in the |
| 13 | * documentation and/or other materials provided with the distribution. |
| 14 | * |
| 15 | * - Neither the name of Sun Microsystems nor the names of its |
| 16 | * contributors may be used to endorse or promote products derived |
| 17 | * from this software without specific prior written permission. |
| 18 | * |
| 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
| 20 | * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| 21 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| 23 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 24 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 25 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| 26 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| 27 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| 28 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| 29 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 | */ |
| 31 | |
| 32 | import java.lang.reflect.*; |
| 33 | import java.io.*; |
| 34 | import java.net.*; |
| 35 | |
| 36 | /** |
| 37 | * This class is provided for access to the underlying poll(2) |
| 38 | * or /dev/poll kernel interfaces. This may be needed for |
| 39 | * multiplexing IO when an application cannot afford to have |
| 40 | * a thread block on each outstanding IO request. |
| 41 | * |
| 42 | * It currently supports the same basic functionality as the |
| 43 | * C poll(2) API, although for efficiency we needed to avoid |
| 44 | * passing the entire pollfd array for every call. See man |
| 45 | * pages for poll(2) for info on C API and event types. |
| 46 | * |
| 47 | * |
| 48 | * @author Bruce Chapman |
| 49 | * @see java.io.FileDescriptor |
| 50 | * @see java.net.Socket |
| 51 | * @see attached README.txt |
| 52 | * @since JDK1.2 |
| 53 | */ |
| 54 | |
| 55 | public class Poller { |
| 56 | /** |
| 57 | * Solaris POLL event types. |
| 58 | */ |
| 59 | public final static short POLLERR = 0x08; |
| 60 | public final static short POLLHUP = 0x10; |
| 61 | public final static short POLLNVAL = 0x20; |
| 62 | public final static short POLLIN = 1; |
| 63 | public final static short POLLPRI = 2; |
| 64 | public final static short POLLOUT = 4; |
| 65 | public final static short POLLRDNORM = 0x40; |
| 66 | public final static short POLLWRNORM = POLLOUT ; |
| 67 | public final static short POLLRDBAND = 0x80; |
| 68 | public final static short POLLWRBAND = 0x100; |
| 69 | public final static short POLLNORM = POLLRDNORM; |
| 70 | |
| 71 | /* |
| 72 | * This global synchronization object must be used for all |
| 73 | * creation or destruction of Poller objects. |
| 74 | */ |
| 75 | private final static Object globalSync = new Object(); |
| 76 | |
| 77 | /* |
| 78 | * The handle for a Poller Object...is used in the JNI C code |
| 79 | * where all the associated data is kept. |
| 80 | */ |
| 81 | private int handle; |
| 82 | |
| 83 | /** |
| 84 | * Constructs an instance of a <code>Poller</code> object. |
| 85 | * Native code uses sysconf(_SC_OPEN_MAX) to determine how |
| 86 | * many fd/skt objects this Poller object can contain. |
| 87 | */ |
| 88 | public Poller() throws Exception { |
| 89 | synchronized(globalSync) { |
| 90 | this.handle = nativeCreatePoller(-1); |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | /** |
| 95 | * Constructs an instance of a <code>Poller</code> object. |
| 96 | * @param maxFd the maximum number of FileDescriptors/Sockets |
| 97 | * this Poller object can contain. |
| 98 | */ |
| 99 | public Poller(int maxFd) throws Exception { |
| 100 | synchronized(globalSync) { |
| 101 | this.handle = nativeCreatePoller(maxFd); |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | /** |
| 106 | * Needed to clean up at the JNI C level when object is GCd. |
| 107 | */ |
| 108 | protected void finalize() throws Throwable { |
| 109 | synchronized(globalSync) { |
| 110 | nativeDestroyPoller(handle); |
| 111 | super.finalize(); |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | /** |
| 116 | * Since we can't guarantee WHEN finalize is called, we may |
| 117 | * recycle on our own. |
| 118 | * @param maxFd the maximum number of FileDescriptors/Sockets |
| 119 | * this Poller object can contain. |
| 120 | */ |
| 121 | public void reset(int maxFd) throws Exception { |
| 122 | synchronized(globalSync) { |
| 123 | nativeDestroyPoller(handle); |
| 124 | this.handle = nativeCreatePoller(maxFd); |
| 125 | } |
| 126 | } |
| 127 | /** |
| 128 | * Since we can't guarantee WHEN finalize is called, we may |
| 129 | * recycle on our own. |
| 130 | */ |
| 131 | public void reset() throws Exception { |
| 132 | synchronized(globalSync) { |
| 133 | nativeDestroyPoller(handle); |
| 134 | this.handle = nativeCreatePoller(-1); |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | /** |
| 139 | * Add FileDescriptor to the set handled by this Poller object. |
| 140 | * |
| 141 | * @param fdObj the FileDescriptor, Socket, or ServerSocket to add. |
| 142 | * @param event the bitmask of events we are interested in. |
| 143 | * @return the OS level fd associated with this IO Object |
| 144 | * (which is what waitMultiple() stores in fds[]) |
| 145 | */ |
| 146 | public synchronized int add(Object fdObj, short event) throws Exception { |
| 147 | return nativeAddFd(handle,findfd(fdObj), event); |
| 148 | } |
| 149 | |
| 150 | /** |
| 151 | * Remove FileDescriptor from the set handled by this Poller object. |
| 152 | * |
| 153 | * Must be called before the fd/skt is closed. |
| 154 | * @param fdObj the FileDescriptor, Socket, or ServerSocket to remove. |
| 155 | * @return true if removal succeeded. |
| 156 | */ |
| 157 | public synchronized boolean remove(Object fdObj) throws Exception { |
| 158 | return (nativeRemoveFd(handle,findfd(fdObj)) == 1); |
| 159 | } |
| 160 | /** |
| 161 | * Check if fd or socket is already in the set handled by this Poller object |
| 162 | * |
| 163 | * @param fdObj the FileDescriptor or [Server]Socket to check. |
| 164 | * @return true if fd/skt is in the set for this Poller object. |
| 165 | */ |
| 166 | public synchronized boolean isMember(Object fdObj) throws Exception { |
| 167 | return (nativeIsMember(handle,findfd(fdObj)) == 1); |
| 168 | } |
| 169 | /** |
| 170 | * Wait on Multiple IO Objects. |
| 171 | * |
| 172 | * @param maxRet the maximum number of fds[] and revents[] to return. |
| 173 | * @param fds[] (return) an array of ints in which to store fds with |
| 174 | * available data upon a successful non-timeout return. |
| 175 | * fds.length must be >= maxRet |
| 176 | * @param revents[] (return) the actual events available on the |
| 177 | * same-indexed fds[] (i.e. fds[0] has events revents[0]) |
| 178 | * revents.length must be >= maxRet |
| 179 | * |
| 180 | * Note : both above arrays are "dense," i.e. only fds[] with events |
| 181 | * available are returned. |
| 182 | * |
| 183 | * @param timeout the maximum number of milliseconds to wait for |
| 184 | * events before timing out. |
| 185 | * @return the number of fds with triggered events. |
| 186 | * |
| 187 | * Note : convenience methods exist for skipping the timeout parameter |
| 188 | * or the maxRet parameter (in the case of no maxRet, fds.length |
| 189 | * must equal revents.length) |
| 190 | * |
| 191 | * obj.waitMultiple(null,null,timeout) can be used for pausing the LWP |
| 192 | * (much more reliable and scalable than Thread.sleep() or Object.wait()) |
| 193 | */ |
| 194 | public synchronized int waitMultiple(int maxRet, int[] fds,short[] revents, |
| 195 | long timeout) throws Exception |
| 196 | { |
| 197 | if ((revents == null) || (fds == null)) { |
| 198 | if (maxRet > 0) { |
| 199 | throw new NullPointerException("fds or revents is null"); |
| 200 | } |
| 201 | } else if ( (maxRet < 0) || |
| 202 | (maxRet > revents.length) || (maxRet > fds.length) ) { |
| 203 | throw new IllegalArgumentException("maxRet out of range"); |
| 204 | } |
| 205 | |
| 206 | int ret = nativeWait(handle, maxRet, fds, revents, timeout); |
| 207 | if (ret < 0) { |
| 208 | throw new InterruptedIOException(); |
| 209 | } |
| 210 | return ret; |
| 211 | } |
| 212 | |
| 213 | /** |
| 214 | * Wait on Multiple IO Objects (no timeout). |
| 215 | * A convenience method for waiting indefinitely on IO events |
| 216 | * |
| 217 | * @see Poller#waitMultiple |
| 218 | * |
| 219 | */ |
| 220 | public int waitMultiple(int maxRet, int[] fds, short[] revents) |
| 221 | throws Exception |
| 222 | { |
| 223 | return waitMultiple(maxRet, fds, revents,-1L); // already synchronized |
| 224 | } |
| 225 | |
| 226 | /** |
| 227 | * Wait on Multiple IO Objects (no maxRet). |
| 228 | * A convenience method for waiting on IO events when the fds |
| 229 | * and revents arrays are the same length and that specifies the |
| 230 | * maximum number of return events. |
| 231 | * |
| 232 | * @see Poller#waitMultiple |
| 233 | * |
| 234 | */ |
| 235 | public synchronized int waitMultiple(int[] fds, short[] revents, |
| 236 | long timeout) throws Exception |
| 237 | { |
| 238 | if ((revents == null) && (fds == null)) { |
| 239 | return nativeWait(handle,0,null,null,timeout); |
| 240 | } else if ((revents == null) || (fds == null)) { |
| 241 | throw new NullPointerException("revents or fds is null"); |
| 242 | } else if (fds.length == revents.length) { |
| 243 | return nativeWait(handle, fds.length, fds, revents, timeout); |
| 244 | } |
| 245 | throw new IllegalArgumentException("fds.length != revents.length"); |
| 246 | } |
| 247 | |
| 248 | |
| 249 | /** |
| 250 | * Wait on Multiple IO Objects (no maxRet/timeout). |
| 251 | * A convenience method for waiting on IO events when the fds |
| 252 | * and revents arrays are the same length and that specifies the |
| 253 | * maximum number of return events, and when waiting indefinitely |
| 254 | * for IO events to occur. |
| 255 | * |
| 256 | * @see Poller#waitMultiple |
| 257 | * |
| 258 | */ |
| 259 | public int waitMultiple(int[] fds, short[] revents) |
| 260 | throws Exception |
| 261 | { |
| 262 | if ((revents == null) || (fds == null)) { |
| 263 | throw new NullPointerException("fds or revents is null"); |
| 264 | } else if (fds.length == revents.length) { |
| 265 | return waitMultiple(revents.length,fds,revents,-1L); // already sync |
| 266 | } |
| 267 | throw new IllegalArgumentException("fds.length != revents.length"); |
| 268 | } |
| 269 | |
| 270 | // Utility - get (int) fd from FileDescriptor or [Server]Socket objects. |
| 271 | |
| 272 | private int findfd(Object fdObj) throws Exception { |
| 273 | Class cl; |
| 274 | Field f; |
| 275 | Object val, implVal; |
| 276 | |
| 277 | if ((fdObj instanceof java.net.Socket) || |
| 278 | (fdObj instanceof java.net.ServerSocket)) { |
| 279 | cl = fdObj.getClass(); |
| 280 | f = cl.getDeclaredField("impl"); |
| 281 | f.setAccessible(true); |
| 282 | val = f.get(fdObj); |
| 283 | cl = f.getType(); |
| 284 | f = cl.getDeclaredField("fd"); |
| 285 | f.setAccessible(true); |
| 286 | implVal = f.get(val); |
| 287 | cl = f.getType(); |
| 288 | f = cl.getDeclaredField("fd"); |
| 289 | f.setAccessible(true); |
| 290 | return ((Integer) f.get(implVal)).intValue(); |
| 291 | } else if ( fdObj instanceof java.io.FileDescriptor ) { |
| 292 | cl = fdObj.getClass(); |
| 293 | f = cl.getDeclaredField("fd"); |
| 294 | f.setAccessible(true); |
| 295 | return ((Integer) f.get(fdObj)).intValue(); |
| 296 | } |
| 297 | else { |
| 298 | throw new IllegalArgumentException("Illegal Object type."); |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | // Actual NATIVE calls |
| 303 | |
| 304 | private static native int nativeInit(); |
| 305 | private native int nativeCreatePoller(int maxFd) throws Exception; |
| 306 | private native void nativeDestroyPoller(int handle) throws Exception; |
| 307 | private native int nativeAddFd(int handle, int fd, short events) |
| 308 | throws Exception; |
| 309 | private native int nativeRemoveFd(int handle, int fd) throws Exception; |
| 310 | private native int nativeRemoveIndex(int handle, int index) |
| 311 | throws Exception; |
| 312 | private native int nativeIsMember(int handle, int fd) throws Exception; |
| 313 | private native int nativeWait(int handle, int maxRet, int[] fds, |
| 314 | short[] events, long timeout) |
| 315 | throws Exception; |
| 316 | /** |
| 317 | * Get number of active CPUs in this machine |
| 318 | * to determine proper level of concurrency. |
| 319 | */ |
| 320 | public static native int getNumCPUs(); |
| 321 | |
| 322 | static { |
| 323 | System.loadLibrary("poller"); |
| 324 | nativeInit(); |
| 325 | } |
| 326 | } |