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