001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.util;
019
020import java.lang.reflect.Field;
021import java.lang.reflect.Method;
022import java.security.AccessController;
023import java.security.PrivilegedAction;
024
025import org.apache.yetus.audience.InterfaceAudience;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028
029@InterfaceAudience.Private
030public class UnsafeAvailChecker {
031
032  private static final String CLASS_NAME = "sun.misc.Unsafe";
033  private static final Logger LOG = LoggerFactory.getLogger(UnsafeAvailChecker.class);
034  private static boolean avail = false;
035  private static boolean unaligned = false;
036
037  static {
038    avail = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
039      @Override
040      public Boolean run() {
041        try {
042          Class<?> clazz = Class.forName(CLASS_NAME);
043          Field f = clazz.getDeclaredField("theUnsafe");
044          f.setAccessible(true);
045          Object theUnsafe = f.get(null);
046          if (theUnsafe == null) {
047            LOG.warn("Could not get static instance from sun.misc.Unsafe");
048            return false;
049          }
050          // Check for availability of all methods used by UnsafeAccess
051          Method m;
052          try {
053            m = clazz.getDeclaredMethod("arrayBaseOffset", Class.class);
054            if (m == null) {
055              LOG.warn("sun.misc.Unsafe is missing arrayBaseOffset(Class)");
056              return false;
057            }
058            m = clazz.getDeclaredMethod("copyMemory", Object.class, long.class, Object.class,
059              long.class, long.class);
060            if (m == null) {
061              LOG.warn("sun.misc.Unsafe is missing copyMemory(Object,long,Object,long,long)");
062              return false;
063            }
064            m = clazz.getDeclaredMethod("getByte", Object.class, long.class);
065            if (m == null) {
066              LOG.warn("sun.misc.Unsafe is missing getByte(Object,long)");
067              return false;
068            }
069            m = clazz.getDeclaredMethod("getShort", long.class);
070            if (m == null) {
071              LOG.warn("sun.misc.Unsafe is missing getShort(long)");
072              return false;
073            }
074            m = clazz.getDeclaredMethod("getShort", Object.class, long.class);
075            if (m == null) {
076              LOG.warn("sun.misc.Unsafe is missing getShort(Object,long)");
077              return false;
078            }
079            m = clazz.getDeclaredMethod("getInt", long.class);
080            if (m == null) {
081              LOG.warn("sun.misc.Unsafe is missing getInt(long)");
082              return false;
083            }
084            m = clazz.getDeclaredMethod("getInt", Object.class, long.class);
085            if (m == null) {
086              LOG.warn("sun.misc.Unsafe is missing getInt(Object,long)");
087              return false;
088            }
089            m = clazz.getDeclaredMethod("getLong", long.class);
090            if (m == null) {
091              LOG.warn("sun.misc.Unsafe is missing getLong(long)");
092              return false;
093            }
094            m = clazz.getDeclaredMethod("getLong", Object.class, long.class);
095            if (m == null) {
096              LOG.warn("sun.misc.Unsafe is missing getLong(Object,long)");
097              return false;
098            }
099            m = clazz.getDeclaredMethod("putByte", long.class, byte.class);
100            if (m == null) {
101              LOG.warn("sun.misc.Unsafe is missing putByte(long,byte)");
102              return false;
103            }
104            m = clazz.getDeclaredMethod("putByte", Object.class, long.class, byte.class);
105            if (m == null) {
106              LOG.warn("sun.misc.Unsafe is missing putByte(Object,long,byte)");
107              return false;
108            }
109            m = clazz.getDeclaredMethod("putShort", long.class, short.class);
110            if (m == null) {
111              LOG.warn("sun.misc.Unsafe is missing putShort(long,short)");
112              return false;
113            }
114            m = clazz.getDeclaredMethod("putShort", Object.class, long.class, short.class);
115            if (m == null) {
116              LOG.warn("sun.misc.Unsafe is missing putShort(Object,long,short)");
117              return false;
118            }
119            m = clazz.getDeclaredMethod("putInt", long.class, int.class);
120            if (m == null) {
121              LOG.warn("sun.misc.Unsafe is missing putInt(long,int)");
122              return false;
123            }
124            m = clazz.getDeclaredMethod("putInt", Object.class, long.class, int.class);
125            if (m == null) {
126              LOG.warn("sun.misc.Unsafe is missing putInt(Object,long,int)");
127              return false;
128            }
129            m = clazz.getDeclaredMethod("putLong", long.class, long.class);
130            if (m == null) {
131              LOG.warn("sun.misc.Unsafe is missing putLong(long,long)");
132              return false;
133            }
134            m = clazz.getDeclaredMethod("putLong", Object.class, long.class, long.class);
135            if (m == null) {
136              LOG.warn("sun.misc.Unsafe is missing putLong(Object,long,long)");
137              return false;
138            }
139            // theUnsafe is accessible and all methods are available
140            return true;
141          } catch (Throwable e) {
142            LOG.warn("sun.misc.Unsafe is missing one or more required methods", e);
143          }
144        } catch (Throwable e) {
145          LOG.warn("sun.misc.Unsafe is not available/accessible", e);
146        }
147        return false;
148      }
149    });
150    // When Unsafe itself is not available/accessible consider unaligned as false.
151    if (avail) {
152      String arch = System.getProperty("os.arch");
153      if ("ppc64".equals(arch) || "ppc64le".equals(arch) || "aarch64".equals(arch)) {
154        // java.nio.Bits.unaligned() wrongly returns false on ppc (JDK-8165231),
155        unaligned = true;
156      } else {
157        try {
158          // Using java.nio.Bits#unaligned() to check for unaligned-access capability
159          Class<?> clazz = Class.forName("java.nio.Bits");
160          Method m = clazz.getDeclaredMethod("unaligned");
161          m.setAccessible(true);
162          unaligned = (Boolean) m.invoke(null);
163        } catch (Exception e) {
164          LOG.warn("java.nio.Bits#unaligned() check failed."
165              + "Unsafe based read/write of primitive types won't be used", e);
166        }
167      }
168    }
169  }
170
171  /**
172   * @return true when running JVM is having sun's Unsafe package available in it and it is
173   *         accessible.
174   */
175  public static boolean isAvailable() {
176    return avail;
177  }
178
179  /**
180   * @return true when running JVM is having sun's Unsafe package available in it and underlying
181   *         system having unaligned-access capability.
182   */
183  public static boolean unaligned() {
184    return unaligned;
185  }
186
187  private UnsafeAvailChecker() {
188    // private constructor to avoid instantiation
189  }
190}