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