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