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