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 }