1 /*
   2  * Copyright (c) 2000, 2019, 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                                              boolean.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                                                 boolean isSync)
 436     {
 437         MappedByteBuffer dbb;
 438         if (directByteBufferConstructor == null)
 439             initDBBConstructor();
 440         try {
 441             dbb = (MappedByteBuffer)directByteBufferConstructor.newInstance(
 442               new Object[] { size,
 443                              addr,
 444                              fd,
 445                              unmapper,
 446                              isSync});
 447         } catch (InstantiationException |
 448                  IllegalAccessException |
 449                  InvocationTargetException e) {
 450             throw new InternalError(e);
 451         }
 452         return dbb;
 453     }
 454 
 455     private static volatile Constructor<?> directByteBufferRConstructor;
 456 
 457     private static void initDBBRConstructor() {
 458         AccessController.doPrivileged(new PrivilegedAction<Void>() {
 459                 public Void run() {
 460                     try {
 461                         Class<?> cl = Class.forName("java.nio.DirectByteBufferR");
 462                         Constructor<?> ctor = cl.getDeclaredConstructor(
 463                             new Class<?>[] { int.class,
 464                                              long.class,
 465                                              FileDescriptor.class,
 466                                              Runnable.class,
 467                                              boolean.class });
 468                         ctor.setAccessible(true);
 469                         directByteBufferRConstructor = ctor;
 470                     } catch (ClassNotFoundException |
 471                              NoSuchMethodException |
 472                              IllegalArgumentException |
 473                              ClassCastException x) {
 474                         throw new InternalError(x);
 475                     }
 476                     return null;
 477                 }});
 478     }
 479 
 480     static MappedByteBuffer newMappedByteBufferR(int size, long addr,
 481                                                  FileDescriptor fd,
 482                                                  Runnable unmapper,
 483                                                  boolean isSync)
 484     {
 485         MappedByteBuffer dbb;
 486         if (directByteBufferRConstructor == null)
 487             initDBBRConstructor();
 488         try {
 489             dbb = (MappedByteBuffer)directByteBufferRConstructor.newInstance(
 490               new Object[] { size,
 491                              addr,
 492                              fd,
 493                              unmapper,
 494                              isSync});
 495         } catch (InstantiationException |
 496                  IllegalAccessException |
 497                  InvocationTargetException e) {
 498             throw new InternalError(e);
 499         }
 500         return dbb;
 501     }
 502 
 503     static void checkBufferPositionAligned(ByteBuffer bb,
 504                                                      int pos, int alignment)
 505         throws IOException
 506     {
 507         if (bb.alignmentOffset(pos, alignment) != 0) {
 508             throw new IOException("Current location of the bytebuffer ("
 509                 + pos + ") is not a multiple of the block size ("
 510                 + alignment + ")");
 511         }
 512     }
 513 
 514     static void checkRemainingBufferSizeAligned(int rem,
 515                                                           int alignment)
 516         throws IOException
 517     {
 518         if (rem % alignment != 0) {
 519             throw new IOException("Number of remaining bytes ("
 520                 + rem + ") is not a multiple of the block size ("
 521                 + alignment + ")");
 522         }
 523     }
 524 
 525     static void checkChannelPositionAligned(long position,
 526                                                       int alignment)
 527         throws IOException
 528     {
 529         if (position % alignment != 0) {
 530            throw new IOException("Channel position (" + position
 531                + ") is not a multiple of the block size ("
 532                + alignment + ")");
 533         }
 534     }
 535 }