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