View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.util;
19  
20  import java.lang.reflect.Field;
21  import java.lang.reflect.Method;
22  import java.nio.ByteBuffer;
23  import java.nio.ByteOrder;
24  import java.security.AccessController;
25  import java.security.PrivilegedAction;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.hadoop.hbase.classification.InterfaceAudience;
30  import org.apache.hadoop.hbase.classification.InterfaceStability;
31  
32  import sun.misc.Unsafe;
33  import sun.nio.ch.DirectBuffer;
34  
35  @InterfaceAudience.Private
36  @InterfaceStability.Evolving
37  public final class UnsafeAccess {
38  
39    private static final Log LOG = LogFactory.getLog(UnsafeAccess.class);
40  
41    public static final Unsafe theUnsafe;
42    private static boolean unaligned = false;
43  
44    /** The offset to the first element in a byte array. */
45    public static final int BYTE_ARRAY_BASE_OFFSET;
46  
47    // This number limits the number of bytes to copy per call to Unsafe's
48    // copyMemory method. A limit is imposed to allow for safepoint polling
49    // during a large copy
50    static final long UNSAFE_COPY_THRESHOLD = 1024L * 1024L;
51    static {
52      theUnsafe = (Unsafe) AccessController.doPrivileged(new PrivilegedAction<Object>() {
53        @Override
54        public Object run() {
55          try {
56            Field f = Unsafe.class.getDeclaredField("theUnsafe");
57            f.setAccessible(true);
58            return f.get(null);
59          } catch (Throwable e) {
60            LOG.warn("sun.misc.Unsafe is not accessible", e);
61          }
62          return null;
63        }
64      });
65  
66      if(theUnsafe != null){
67        BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class);
68        String arch = System.getProperty("os.arch");
69        if ("ppc64".equals(arch) || "ppc64le".equals(arch)) {
70          // java.nio.Bits.unaligned() wrongly returns false on ppc (JDK-8165231),
71          unaligned = true;
72        }
73        else {
74          try {
75            // Using java.nio.Bits#unaligned() to check for unaligned-access capability
76            Class<?> clazz = Class.forName("java.nio.Bits");
77            Method m = clazz.getDeclaredMethod("unaligned");
78            m.setAccessible(true);
79            unaligned = (boolean) m.invoke(null);
80  
81            //Handle the issue fixed in JDK for unaligned value returned.
82            //see this JIRA for more detail <a>https://bugs.openjdk.java.net/browse/JDK-8165231<a>
83            if (System.getProperty("os.arch").equalsIgnoreCase("ppc64le")) {
84              unaligned = true;
85            }
86          } catch (Exception e) {
87            unaligned = false;
88          }
89        }
90      } else{
91        BYTE_ARRAY_BASE_OFFSET = -1;
92      }
93    }
94  
95    private UnsafeAccess(){}
96    
97    public static boolean isAvailable() {
98      return theUnsafe != null;
99    }
100 
101   /**
102    * @return true when running JVM is having sun's Unsafe package available in it and underlying
103    *         system having unaligned-access capability.
104    */
105   public static boolean unaligned() {
106     return unaligned;
107   }
108 
109 
110   // APIs to copy data. This will be direct memory location copy and will be much faster
111   /**
112    * Copies the bytes from given array's offset to length part into the given buffer.
113    * @param src
114    * @param srcOffset
115    * @param dest
116    * @param destOffset
117    * @param length
118    */
119   public static void copy(byte[] src, int srcOffset, ByteBuffer dest, int destOffset, int length) {
120     long destAddress = destOffset;
121     Object destBase = null;
122     if (dest.isDirect()) {
123       destAddress = destAddress + ((DirectBuffer) dest).address();
124     } else {
125       destAddress = destAddress + BYTE_ARRAY_BASE_OFFSET + dest.arrayOffset();
126       destBase = dest.array();
127     }
128     long srcAddress = srcOffset + BYTE_ARRAY_BASE_OFFSET;
129     unsafeCopy(src, srcAddress, destBase, destAddress, length);
130   }
131 
132   private static void unsafeCopy(Object src, long srcAddr, Object dst, long destAddr, long len) {
133     while (len > 0) {
134       long size = (len > UNSAFE_COPY_THRESHOLD) ? UNSAFE_COPY_THRESHOLD : len;
135       theUnsafe.copyMemory(src, srcAddr, dst, destAddr, len);
136       len -= size;
137       srcAddr += size;
138       destAddr += size;
139     }
140   }
141 
142   /**
143    * Copies specified number of bytes from given offset of {@code src} ByteBuffer to the
144    * {@code dest} array.
145    *
146    * @param src
147    * @param srcOffset
148    * @param dest
149    * @param destOffset
150    * @param length
151    */
152   public static void copy(ByteBuffer src, int srcOffset, byte[] dest, int destOffset,
153       int length) {
154     long srcAddress = srcOffset;
155     Object srcBase = null;
156     if (src.isDirect()) {
157       srcAddress = srcAddress + ((DirectBuffer) src).address();
158     } else {
159       srcAddress = srcAddress + BYTE_ARRAY_BASE_OFFSET + src.arrayOffset();
160       srcBase = src.array();
161     }
162     long destAddress = destOffset + BYTE_ARRAY_BASE_OFFSET;
163     unsafeCopy(srcBase, srcAddress, dest, destAddress, length);
164   }
165 
166   /**
167    * Copies specified number of bytes from given offset of {@code src} buffer into the {@code dest}
168    * buffer.
169    *
170    * @param src
171    * @param srcOffset
172    * @param dest
173    * @param destOffset
174    * @param length
175    */
176   public static void copy(ByteBuffer src, int srcOffset, ByteBuffer dest, int destOffset,
177       int length) {
178     long srcAddress, destAddress;
179     Object srcBase = null, destBase = null;
180     if (src.isDirect()) {
181       srcAddress = srcOffset + ((DirectBuffer) src).address();
182     } else {
183       srcAddress = srcOffset +  src.arrayOffset() + BYTE_ARRAY_BASE_OFFSET;
184       srcBase = src.array();
185     }
186     if (dest.isDirect()) {
187       destAddress = destOffset + ((DirectBuffer) dest).address();
188     } else {
189       destAddress = destOffset + BYTE_ARRAY_BASE_OFFSET + dest.arrayOffset();
190       destBase = dest.array();
191     }
192     unsafeCopy(srcBase, srcAddress, destBase, destAddress, length);
193   }
194 }