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