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