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