1 /* 2 * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.nio.ch; 27 28 import java.lang.ref.SoftReference; 29 import java.lang.reflect.*; 30 import java.io.IOException; 31 import java.io.FileDescriptor; 32 import java.nio.ByteBuffer; 33 import java.nio.MappedByteBuffer; 34 import java.nio.channels.*; 35 import java.security.AccessController; 36 import java.security.PrivilegedAction; 37 import java.util.*; 38 import sun.misc.Unsafe; 39 import sun.misc.Cleaner; 40 import sun.security.action.GetPropertyAction; 41 42 43 class Util { 44 45 // -- Caches -- 46 47 // The number of temp buffers in our pool 48 private static final int TEMP_BUF_POOL_SIZE = 8; 49 50 // Per-thread cache of temporary direct buffers 51 private static ThreadLocal<BufferCache> bufferCache = 52 new ThreadLocal<BufferCache>() 53 { 54 @Override 55 protected BufferCache initialValue() { 56 return new BufferCache(); 57 } 58 }; 59 60 /** 61 * A simple cache of direct buffers. 62 */ 63 private static class BufferCache { 64 // the array of buffers 65 private ByteBuffer[] buffers; 66 67 // the number of buffers in the cache 68 private int count; 69 70 // the index of the first valid buffer (undefined if count == 0) 71 private int start; 72 73 private int next(int i) { 74 return (i + 1) % TEMP_BUF_POOL_SIZE; 75 } 76 77 BufferCache() { 78 buffers = new ByteBuffer[TEMP_BUF_POOL_SIZE]; 79 } 80 81 /** 82 * Removes and returns a buffer from the cache of at least the given 83 * size (or null if no suitable buffer is found). 84 */ 85 ByteBuffer get(int size) { 86 if (count == 0) 87 return null; // cache is empty 88 89 ByteBuffer[] buffers = this.buffers; 90 91 // search for suitable buffer (often the first buffer will do) 92 ByteBuffer buf = buffers[start]; 93 if (buf.capacity() < size) { 94 buf = null; 95 int i = start; 96 while ((i = next(i)) != start) { 97 ByteBuffer bb = buffers[i]; 98 if (bb == null) 99 break; 100 if (bb.capacity() >= size) { 101 buf = bb; 102 break; 103 } 104 } 105 if (buf == null) 106 return null; 107 // move first element to here to avoid re-packing 108 buffers[i] = buffers[start]; 109 } 110 111 // remove first element 112 buffers[start] = null; 113 start = next(start); 114 count--; 115 116 // prepare the buffer and return it 117 buf.rewind(); 118 buf.limit(size); 119 return buf; 120 } 121 122 boolean offerFirst(ByteBuffer buf) { 123 if (count >= TEMP_BUF_POOL_SIZE) { 124 return false; 125 } else { 126 start = (start + TEMP_BUF_POOL_SIZE - 1) % TEMP_BUF_POOL_SIZE; 127 buffers[start] = buf; 128 count++; 129 return true; 130 } 131 } 132 133 boolean offerLast(ByteBuffer buf) { 134 if (count >= TEMP_BUF_POOL_SIZE) { 135 return false; 136 } else { 137 int next = (start + count) % TEMP_BUF_POOL_SIZE; 138 buffers[next] = buf; 139 count++; 140 return true; 141 } 142 } 143 144 boolean isEmpty() { 145 return count == 0; 146 } 147 148 ByteBuffer removeFirst() { 149 assert count > 0; 150 ByteBuffer buf = buffers[start]; 151 buffers[start] = null; 152 start = next(start); 153 count--; 154 return buf; 155 } 156 } 157 158 /** 159 * Returns a temporary buffer of at least the given size 160 */ 161 static ByteBuffer getTemporaryDirectBuffer(int size) { 162 BufferCache cache = bufferCache.get(); 163 ByteBuffer buf = cache.get(size); 164 if (buf != null) { 165 return buf; 166 } else { 167 // No suitable buffer in the cache so we need to allocate a new 168 // one. To avoid the cache growing then we remove the first 169 // buffer from the cache and free it. 170 if (!cache.isEmpty()) { 171 buf = cache.removeFirst(); 172 free(buf); 173 } 174 return ByteBuffer.allocateDirect(size); 175 } 176 } 177 178 /** 179 * Releases a temporary buffer by returning to the cache or freeing it. 180 */ 181 static void releaseTemporaryDirectBuffer(ByteBuffer buf) { 182 offerFirstTemporaryDirectBuffer(buf); 183 } 184 185 /** 186 * Releases a temporary buffer by returning to the cache or freeing it. If 187 * returning to the cache then insert it at the start so that it is 188 * likely to be returned by a subsequent call to getTemporaryDirectBuffer. 189 */ 190 static void offerFirstTemporaryDirectBuffer(ByteBuffer buf) { 191 assert buf != null; 192 BufferCache cache = bufferCache.get(); 193 if (!cache.offerFirst(buf)) { 194 // cache is full 195 free(buf); 196 } 197 } 198 199 /** 200 * Releases a temporary buffer by returning to the cache or freeing it. If 201 * returning to the cache then insert it at the end. This makes it 202 * suitable for scatter/gather operations where the buffers are returned to 203 * cache in same order that they were obtained. 204 */ 205 static void offerLastTemporaryDirectBuffer(ByteBuffer buf) { 206 assert buf != null; 207 BufferCache cache = bufferCache.get(); 208 if (!cache.offerLast(buf)) { 209 // cache is full 210 free(buf); 211 } 212 } 213 214 /** 215 * Frees the memory for the given direct buffer 216 */ 217 private static void free(ByteBuffer buf) { 218 ((DirectBuffer)buf).cleaner().clean(); 219 } 220 221 private static class SelectorWrapper { 222 private Selector sel; 223 private SelectorWrapper (Selector sel) { 224 this.sel = sel; 225 Cleaner.create(this, new Closer(sel)); 226 } 227 private static class Closer implements Runnable { 228 private Selector sel; 229 private Closer (Selector sel) { 230 this.sel = sel; 231 } 232 public void run () { 233 try { 234 sel.close(); 235 } catch (Throwable th) { 236 throw new Error(th); 237 } 238 } 239 } 240 public Selector get() { return sel;} 241 } 242 243 // Per-thread cached selector 244 private static ThreadLocal<SoftReference<SelectorWrapper>> localSelector 245 = new ThreadLocal<SoftReference<SelectorWrapper>>(); 246 // Hold a reference to the selWrapper object to prevent it from 247 // being cleaned when the temporary selector wrapped is on lease. 248 private static ThreadLocal<SelectorWrapper> localSelectorWrapper 249 = new ThreadLocal<SelectorWrapper>(); 250 251 // When finished, invoker must ensure that selector is empty 252 // by cancelling any related keys and explicitly releasing 253 // the selector by invoking releaseTemporarySelector() 254 static Selector getTemporarySelector(SelectableChannel sc) 255 throws IOException 256 { 257 SoftReference<SelectorWrapper> ref = localSelector.get(); 258 SelectorWrapper selWrapper = null; 259 Selector sel = null; 260 if (ref == null 261 || ((selWrapper = ref.get()) == null) 262 || ((sel = selWrapper.get()) == null) 263 || (sel.provider() != sc.provider())) { 264 sel = sc.provider().openSelector(); 265 selWrapper = new SelectorWrapper(sel); 266 localSelector.set(new SoftReference<SelectorWrapper>(selWrapper)); 267 } 268 localSelectorWrapper.set(selWrapper); 269 return sel; 270 } 271 272 static void releaseTemporarySelector(Selector sel) 273 throws IOException 274 { 275 // Selector should be empty 276 sel.selectNow(); // Flush cancelled keys 277 assert sel.keys().isEmpty() : "Temporary selector not empty"; 278 localSelectorWrapper.set(null); 279 } 280 281 282 // -- Random stuff -- 283 284 static ByteBuffer[] subsequence(ByteBuffer[] bs, int offset, int length) { 285 if ((offset == 0) && (length == bs.length)) 286 return bs; 287 int n = length; 288 ByteBuffer[] bs2 = new ByteBuffer[n]; 289 for (int i = 0; i < n; i++) 290 bs2[i] = bs[offset + i]; 291 return bs2; 292 } 293 294 static <E> Set<E> ungrowableSet(final Set<E> s) { 295 return new Set<E>() { 296 297 public int size() { return s.size(); } 298 public boolean isEmpty() { return s.isEmpty(); } 299 public boolean contains(Object o) { return s.contains(o); } 300 public Object[] toArray() { return s.toArray(); } 301 public <T> T[] toArray(T[] a) { return s.toArray(a); } 302 public String toString() { return s.toString(); } 303 public Iterator<E> iterator() { return s.iterator(); } 304 public boolean equals(Object o) { return s.equals(o); } 305 public int hashCode() { return s.hashCode(); } 306 public void clear() { s.clear(); } 307 public boolean remove(Object o) { return s.remove(o); } 308 309 public boolean containsAll(Collection<?> coll) { 310 return s.containsAll(coll); 311 } 312 public boolean removeAll(Collection<?> coll) { 313 return s.removeAll(coll); 314 } 315 public boolean retainAll(Collection<?> coll) { 316 return s.retainAll(coll); 317 } 318 319 public boolean add(E o){ 320 throw new UnsupportedOperationException(); 321 } 322 public boolean addAll(Collection<? extends E> coll) { 323 throw new UnsupportedOperationException(); 324 } 325 326 }; 327 } 328 329 330 // -- Unsafe access -- 331 332 private static Unsafe unsafe = Unsafe.getUnsafe(); 333 334 private static byte _get(long a) { 335 return unsafe.getByte(a); 336 } 337 338 private static void _put(long a, byte b) { 339 unsafe.putByte(a, b); 340 } 341 342 static void erase(ByteBuffer bb) { 343 unsafe.setMemory(((DirectBuffer)bb).address(), bb.capacity(), (byte)0); 344 } 345 346 static Unsafe unsafe() { 347 return unsafe; 348 } 349 350 private static int pageSize = -1; 351 352 static int pageSize() { 353 if (pageSize == -1) 354 pageSize = unsafe().pageSize(); 355 return pageSize; 356 } 357 358 private static volatile Constructor directByteBufferConstructor = null; 359 360 private static void initDBBConstructor() { 361 AccessController.doPrivileged(new PrivilegedAction<Void>() { 362 public Void run() { 363 try { 364 Class<?> cl = Class.forName("java.nio.DirectByteBuffer"); 365 Constructor ctor = cl.getDeclaredConstructor( 366 new Class[] { int.class, 367 long.class, 368 FileDescriptor.class, 369 Runnable.class }); 370 ctor.setAccessible(true); 371 directByteBufferConstructor = ctor; 372 } catch (ClassNotFoundException x) { 373 throw new InternalError(); 374 } catch (NoSuchMethodException x) { 375 throw new InternalError(); 376 } catch (IllegalArgumentException x) { 377 throw new InternalError(); 378 } catch (ClassCastException x) { 379 throw new InternalError(); 380 } 381 return null; 382 }}); 383 } 384 385 static MappedByteBuffer newMappedByteBuffer(int size, long addr, 386 FileDescriptor fd, 387 Runnable unmapper) 388 { 389 MappedByteBuffer dbb; 390 if (directByteBufferConstructor == null) 391 initDBBConstructor(); 392 try { 393 dbb = (MappedByteBuffer)directByteBufferConstructor.newInstance( 394 new Object[] { new Integer(size), 395 new Long(addr), 396 fd, 397 unmapper }); 398 } catch (InstantiationException e) { 399 throw new InternalError(); 400 } catch (IllegalAccessException e) { 401 throw new InternalError(); 402 } catch (InvocationTargetException e) { 403 throw new InternalError(); 404 } 405 return dbb; 406 } 407 408 private static volatile Constructor directByteBufferRConstructor = null; 409 410 private static void initDBBRConstructor() { 411 AccessController.doPrivileged(new PrivilegedAction<Void>() { 412 public Void run() { 413 try { 414 Class<?> cl = Class.forName("java.nio.DirectByteBufferR"); 415 Constructor ctor = cl.getDeclaredConstructor( 416 new Class[] { int.class, 417 long.class, 418 FileDescriptor.class, 419 Runnable.class }); 420 ctor.setAccessible(true); 421 directByteBufferRConstructor = ctor; 422 } catch (ClassNotFoundException x) { 423 throw new InternalError(); 424 } catch (NoSuchMethodException x) { 425 throw new InternalError(); 426 } catch (IllegalArgumentException x) { 427 throw new InternalError(); 428 } catch (ClassCastException x) { 429 throw new InternalError(); 430 } 431 return null; 432 }}); 433 } 434 435 static MappedByteBuffer newMappedByteBufferR(int size, long addr, 436 FileDescriptor fd, 437 Runnable unmapper) 438 { 439 MappedByteBuffer dbb; 440 if (directByteBufferRConstructor == null) 441 initDBBRConstructor(); 442 try { 443 dbb = (MappedByteBuffer)directByteBufferRConstructor.newInstance( 444 new Object[] { new Integer(size), 445 new Long(addr), 446 fd, 447 unmapper }); 448 } catch (InstantiationException e) { 449 throw new InternalError(); 450 } catch (IllegalAccessException e) { 451 throw new InternalError(); 452 } catch (InvocationTargetException e) { 453 throw new InternalError(); 454 } 455 return dbb; 456 } 457 458 459 // -- Bug compatibility -- 460 461 private static volatile String bugLevel = null; 462 463 static boolean atBugLevel(String bl) { // package-private 464 if (bugLevel == null) { 465 if (!sun.misc.VM.isBooted()) 466 return false; 467 String value = AccessController.doPrivileged( 468 new GetPropertyAction("sun.nio.ch.bugLevel")); 469 bugLevel = (value != null) ? value : ""; 470 } 471 return bugLevel.equals(bl); 472 } 473 474 475 476 // -- Initialization -- 477 478 private static boolean loaded = false; 479 480 static void load() { 481 synchronized (Util.class) { 482 if (loaded) 483 return; 484 loaded = true; 485 java.security.AccessController 486 .doPrivileged(new sun.security.action.LoadLibraryAction("net")); 487 java.security.AccessController 488 .doPrivileged(new sun.security.action.LoadLibraryAction("nio")); 489 // IOUtil must be initialized; Its native methods are called from 490 // other places in native nio code so they must be set up. 491 IOUtil.initIDs(); 492 } 493 } 494 495 }