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