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