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