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