1 /*
   2  * Copyright (c) 2000, 2013, 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     // Per-thread cache of temporary direct buffers
  48     private static ThreadLocal<BufferCache> bufferCache =
  49         new ThreadLocal<BufferCache>()
  50     {
  51         @Override
  52         protected BufferCache initialValue() {
  53             return new BufferCache();
  54         }
  55     };
  56 
  57     /**
  58      * A simple cache of direct buffers.
  59      */
  60     private static class BufferCache {
  61         // the array of buffers
  62         private ByteBuffer[] buffers;
  63 
  64         // the number of buffers in the cache
  65         private int count;
  66 
  67         // the index of the first valid buffer (undefined if count == 0)
  68         private int start;
  69 
  70         private int next(int i) {
  71             return (i + 1) % TEMP_BUF_POOL_SIZE;
  72         }
  73 
  74         BufferCache() {
  75             buffers = new ByteBuffer[TEMP_BUF_POOL_SIZE];
  76         }
  77 
  78         /**
  79          * Removes and returns a buffer from the cache of at least the given
  80          * size (or null if no suitable buffer is found).
  81          */
  82         ByteBuffer get(int size) {
  83             if (count == 0)
  84                 return null;  // cache is empty
  85 
  86             ByteBuffer[] buffers = this.buffers;
  87 
  88             // search for suitable buffer (often the first buffer will do)
  89             ByteBuffer buf = buffers[start];
  90             if (buf.capacity() < size) {
  91                 buf = null;
  92                 int i = start;
  93                 while ((i = next(i)) != start) {
  94                     ByteBuffer bb = buffers[i];
  95                     if (bb == null)
  96                         break;
  97                     if (bb.capacity() >= size) {
  98                         buf = bb;
  99                         break;
 100                     }
 101                 }
 102                 if (buf == null)
 103                     return null;
 104                 // move first element to here to avoid re-packing
 105                 buffers[i] = buffers[start];
 106             }
 107 
 108             // remove first element
 109             buffers[start] = null;
 110             start = next(start);
 111             count--;
 112 
 113             // prepare the buffer and return it
 114             buf.rewind();
 115             buf.limit(size);
 116             return buf;
 117         }
 118 
 119         boolean offerFirst(ByteBuffer buf) {
 120             if (count >= TEMP_BUF_POOL_SIZE) {
 121                 return false;
 122             } else {
 123                 start = (start + TEMP_BUF_POOL_SIZE - 1) % TEMP_BUF_POOL_SIZE;
 124                 buffers[start] = buf;
 125                 count++;
 126                 return true;
 127             }
 128         }
 129 
 130         boolean offerLast(ByteBuffer buf) {
 131             if (count >= TEMP_BUF_POOL_SIZE) {
 132                 return false;
 133             } else {
 134                 int next = (start + count) % TEMP_BUF_POOL_SIZE;
 135                 buffers[next] = buf;
 136                 count++;
 137                 return true;
 138             }
 139         }
 140 
 141         boolean isEmpty() {
 142             return count == 0;
 143         }
 144 
 145         ByteBuffer removeFirst() {
 146             assert count > 0;
 147             ByteBuffer buf = buffers[start];
 148             buffers[start] = null;
 149             start = next(start);
 150             count--;
 151             return buf;
 152         }
 153     }
 154 
 155     /**
 156      * Returns a temporary buffer of at least the given size
 157      */
 158     public static ByteBuffer getTemporaryDirectBuffer(int size) {
 159         BufferCache cache = bufferCache.get();
 160         ByteBuffer buf = cache.get(size);
 161         if (buf != null) {
 162             return buf;
 163         } else {
 164             // No suitable buffer in the cache so we need to allocate a new
 165             // one. To avoid the cache growing then we remove the first
 166             // buffer from the cache and free it.
 167             if (!cache.isEmpty()) {
 168                 buf = cache.removeFirst();
 169                 free(buf);
 170             }
 171             return ByteBuffer.allocateDirect(size);
 172         }
 173     }
 174 
 175     /**
 176      * Releases a temporary buffer by returning to the cache or freeing it.
 177      */
 178     public static void releaseTemporaryDirectBuffer(ByteBuffer buf) {
 179         offerFirstTemporaryDirectBuffer(buf);
 180     }
 181 
 182     /**
 183      * Releases a temporary buffer by returning to the cache or freeing it. If
 184      * returning to the cache then insert it at the start so that it is
 185      * likely to be returned by a subsequent call to getTemporaryDirectBuffer.
 186      */
 187     static void offerFirstTemporaryDirectBuffer(ByteBuffer buf) {
 188         assert buf != null;
 189         BufferCache cache = bufferCache.get();
 190         if (!cache.offerFirst(buf)) {
 191             // cache is full
 192             free(buf);
 193         }
 194     }
 195 
 196     /**
 197      * Releases a temporary buffer by returning to the cache or freeing it. If
 198      * returning to the cache then insert it at the end. This makes it
 199      * suitable for scatter/gather operations where the buffers are returned to
 200      * cache in same order that they were obtained.
 201      */
 202     static void offerLastTemporaryDirectBuffer(ByteBuffer buf) {
 203         assert buf != null;
 204         BufferCache cache = bufferCache.get();
 205         if (!cache.offerLast(buf)) {
 206             // cache is full
 207             free(buf);
 208         }
 209     }
 210 
 211     /**
 212      * Frees the memory for the given direct buffer
 213      */
 214     private static void free(ByteBuffer buf) {
 215         ((DirectBuffer)buf).cleaner().clean();
 216     }
 217 
 218 
 219     // -- Random stuff --
 220 
 221     static ByteBuffer[] subsequence(ByteBuffer[] bs, int offset, int length) {
 222         if ((offset == 0) && (length == bs.length))
 223             return bs;
 224         int n = length;
 225         ByteBuffer[] bs2 = new ByteBuffer[n];
 226         for (int i = 0; i < n; i++)
 227             bs2[i] = bs[offset + i];
 228         return bs2;
 229     }
 230 
 231     static <E> Set<E> ungrowableSet(final Set<E> s) {
 232         return new Set<E>() {
 233 
 234                 public int size()                 { return s.size(); }
 235                 public boolean isEmpty()          { return s.isEmpty(); }
 236                 public boolean contains(Object o) { return s.contains(o); }
 237                 public Object[] toArray()         { return s.toArray(); }
 238                 public <T> T[] toArray(T[] a)     { return s.toArray(a); }
 239                 public String toString()          { return s.toString(); }
 240                 public Iterator<E> iterator()     { return s.iterator(); }
 241                 public boolean equals(Object o)   { return s.equals(o); }
 242                 public int hashCode()             { return s.hashCode(); }
 243                 public void clear()               { s.clear(); }
 244                 public boolean remove(Object o)   { return s.remove(o); }
 245 
 246                 public boolean containsAll(Collection<?> coll) {
 247                     return s.containsAll(coll);
 248                 }
 249                 public boolean removeAll(Collection<?> coll) {
 250                     return s.removeAll(coll);
 251                 }
 252                 public boolean retainAll(Collection<?> coll) {
 253                     return s.retainAll(coll);
 254                 }
 255 
 256                 public boolean add(E o){
 257                     throw new UnsupportedOperationException();
 258                 }
 259                 public boolean addAll(Collection<? extends E> coll) {
 260                     throw new UnsupportedOperationException();
 261                 }
 262 
 263         };
 264     }
 265 
 266 
 267     // -- Unsafe access --
 268 
 269     private static Unsafe unsafe = Unsafe.getUnsafe();
 270 
 271     private static byte _get(long a) {
 272         return unsafe.getByte(a);
 273     }
 274 
 275     private static void _put(long a, byte b) {
 276         unsafe.putByte(a, b);
 277     }
 278 
 279     static void erase(ByteBuffer bb) {
 280         unsafe.setMemory(((DirectBuffer)bb).address(), bb.capacity(), (byte)0);
 281     }
 282 
 283     static Unsafe unsafe() {
 284         return unsafe;
 285     }
 286 
 287     private static int pageSize = -1;
 288 
 289     static int pageSize() {
 290         if (pageSize == -1)
 291             pageSize = unsafe().pageSize();
 292         return pageSize;
 293     }
 294 
 295     private static volatile Constructor<?> directByteBufferConstructor;
 296 
 297     private static void initDBBConstructor() {
 298         AccessController.doPrivileged(new PrivilegedAction<Void>() {
 299                 public Void run() {
 300                     try {
 301                         Class<?> cl = Class.forName("java.nio.DirectByteBuffer");
 302                         Constructor<?> ctor = cl.getDeclaredConstructor(
 303                             new Class<?>[] { int.class,
 304                                              long.class,
 305                                              FileDescriptor.class,
 306                                              Runnable.class });
 307                         ctor.setAccessible(true);
 308                         directByteBufferConstructor = ctor;
 309                     } catch (ClassNotFoundException   |
 310                              NoSuchMethodException    |
 311                              IllegalArgumentException |
 312                              ClassCastException x) {
 313                         throw new InternalError(x);
 314                     }
 315                     return null;
 316                 }});
 317     }
 318 
 319     static MappedByteBuffer newMappedByteBuffer(int size, long addr,
 320                                                 FileDescriptor fd,
 321                                                 Runnable unmapper)
 322     {
 323         MappedByteBuffer dbb;
 324         if (directByteBufferConstructor == null)
 325             initDBBConstructor();
 326         try {
 327             dbb = (MappedByteBuffer)directByteBufferConstructor.newInstance(
 328               new Object[] { size,
 329                              addr,
 330                              fd,
 331                              unmapper });
 332         } catch (InstantiationException |
 333                  IllegalAccessException |
 334                  InvocationTargetException e) {
 335             throw new InternalError(e);
 336         }
 337         return dbb;
 338     }
 339 
 340     private static volatile Constructor<?> directByteBufferRConstructor;
 341 
 342     private static void initDBBRConstructor() {
 343         AccessController.doPrivileged(new PrivilegedAction<Void>() {
 344                 public Void run() {
 345                     try {
 346                         Class<?> cl = Class.forName("java.nio.DirectByteBufferR");
 347                         Constructor<?> ctor = cl.getDeclaredConstructor(
 348                             new Class<?>[] { int.class,
 349                                              long.class,
 350                                              FileDescriptor.class,
 351                                              Runnable.class });
 352                         ctor.setAccessible(true);
 353                         directByteBufferRConstructor = ctor;
 354                     } catch (ClassNotFoundException |
 355                              NoSuchMethodException |
 356                              IllegalArgumentException |
 357                              ClassCastException x) {
 358                         throw new InternalError(x);
 359                     }
 360                     return null;
 361                 }});
 362     }
 363 
 364     static MappedByteBuffer newMappedByteBufferR(int size, long addr,
 365                                                  FileDescriptor fd,
 366                                                  Runnable unmapper)
 367     {
 368         MappedByteBuffer dbb;
 369         if (directByteBufferRConstructor == null)
 370             initDBBRConstructor();
 371         try {
 372             dbb = (MappedByteBuffer)directByteBufferRConstructor.newInstance(
 373               new Object[] { size,
 374                              addr,
 375                              fd,
 376                              unmapper });
 377         } catch (InstantiationException |
 378                  IllegalAccessException |
 379                  InvocationTargetException e) {
 380             throw new InternalError(e);
 381         }
 382         return dbb;
 383     }
 384 
 385 
 386     // -- Bug compatibility --
 387 
 388     private static volatile String bugLevel;
 389 
 390     static boolean atBugLevel(String bl) {              // package-private
 391         if (bugLevel == null) {
 392             if (!jdk.internal.misc.VM.isBooted())
 393                 return false;
 394             String value = AccessController.doPrivileged(
 395                 new GetPropertyAction("sun.nio.ch.bugLevel"));
 396             bugLevel = (value != null) ? value : "";
 397         }
 398         return bugLevel.equals(bl);
 399     }
 400 
 401 }