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