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