/* * Copyright 2013 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package io.netty_voltpatches.util.internal; import sun.misc.Unsafe; import static io.netty_voltpatches.util.internal.ObjectUtil.checkNotNull; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.Buffer; import java.nio.ByteBuffer; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicLongFieldUpdater; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import io.netty_voltpatches.util.internal.logging.InternalLogger; import io.netty_voltpatches.util.internal.logging.InternalLoggerFactory; /** * The {@link PlatformDependent} operations which requires access to {@code sun.misc.*}. */ final class PlatformDependent0 { private static final InternalLogger logger = InternalLoggerFactory.getInstance(PlatformDependent0.class); static final Unsafe UNSAFE; private static final long ADDRESS_FIELD_OFFSET; private static final long BYTE_ARRAY_BASE_OFFSET; private static final Constructor<?> DIRECT_BUFFER_CONSTRUCTOR; /** * Limits the number of bytes to copy per {@link Unsafe#copyMemory(long, long, long)} to allow safepoint polling * during a large copy. */ private static final long UNSAFE_COPY_THRESHOLD = 1024L * 1024L; private static final boolean UNALIGNED; static { final ByteBuffer direct = ByteBuffer.allocateDirect(1); final Field addressField; // attempt to access field Buffer#address final Object maybeAddressField = AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { try { final Field field = Buffer.class.getDeclaredField("address"); field.setAccessible(true); // if direct really is a direct buffer, address will be non-zero if (field.getLong(direct) == 0) { return null; } return field; } catch (IllegalAccessException e) { return e; } catch (NoSuchFieldException e) { return e; } catch (SecurityException e) { return e; } } }); if (maybeAddressField instanceof Field) { addressField = (Field) maybeAddressField; logger.debug("java.nio.Buffer.address: available"); } else { logger.debug("java.nio.Buffer.address: unavailable", (Exception) maybeAddressField); addressField = null; } Unsafe unsafe; if (addressField != null) { // attempt to access field Unsafe#theUnsafe final Object maybeUnsafe = AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { try { final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); unsafeField.setAccessible(true); // the unsafe instance return unsafeField.get(null); } catch (NoSuchFieldException e) { return e; } catch (SecurityException e) { return e; } catch (IllegalAccessException e) { return e; } } }); // the conditional check here can not be replaced with checking that maybeUnsafe // is an instanceof Unsafe and reversing the if and else blocks; this is because an // instanceof check against Unsafe will trigger a class load and we might not have // the runtime permission accessClassInPackage.sun.misc if (maybeUnsafe instanceof Exception) { unsafe = null; logger.debug("sun.misc.Unsafe.theUnsafe: unavailable", (Exception) maybeUnsafe); } else { unsafe = (Unsafe) maybeUnsafe; logger.debug("sun.misc.Unsafe.theUnsafe: available"); } // ensure the unsafe supports all necessary methods to work around the mistake in the latest OpenJDK // https://github.com/netty/netty/issues/1061 // http://www.mail-archive.com/jdk6-dev@openjdk.java.net/msg00698.html if (unsafe != null) { final Unsafe finalUnsafe = unsafe; final Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { try { finalUnsafe.getClass().getDeclaredMethod( "copyMemory", Object.class, long.class, Object.class, long.class, long.class); return null; } catch (NoSuchMethodException e) { return e; } catch (SecurityException e) { return e; } } }); if (maybeException == null) { logger.debug("sun.misc.Unsafe.copyMemory: available"); } else { // Unsafe.copyMemory(Object, long, Object, long, long) unavailable. unsafe = null; logger.debug("sun.misc.Unsafe.copyMemory: unavailable", (Exception) maybeException); } } } else { // If we cannot access the address of a direct buffer, there's no point of using unsafe. // Let's just pretend unsafe is unavailable for overall simplicity. unsafe = null; } UNSAFE = unsafe; if (unsafe == null) { BYTE_ARRAY_BASE_OFFSET = -1; ADDRESS_FIELD_OFFSET = -1; UNALIGNED = false; DIRECT_BUFFER_CONSTRUCTOR = null; } else { Constructor<?> directBufferConstructor; long address = -1; try { final Object maybeDirectBufferConstructor = AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { try { final Constructor constructor = direct.getClass().getDeclaredConstructor(long.class, int.class); constructor.setAccessible(true); return constructor; } catch (NoSuchMethodException e) { return e; } catch (SecurityException e) { return e; } } }); if (maybeDirectBufferConstructor instanceof Constructor<?>) { address = UNSAFE.allocateMemory(1); // try to use the constructor now try { ((Constructor) maybeDirectBufferConstructor).newInstance(address, 1); directBufferConstructor = (Constructor<?>) maybeDirectBufferConstructor; logger.debug("direct buffer constructor: available"); } catch (InstantiationException e) { directBufferConstructor = null; } catch (IllegalAccessException e) { directBufferConstructor = null; } catch (InvocationTargetException e) { directBufferConstructor = null; } } else { logger.debug( "direct buffer constructor: unavailable", (Exception) maybeDirectBufferConstructor); directBufferConstructor = null; } } finally { if (address != -1) { UNSAFE.freeMemory(address); } } DIRECT_BUFFER_CONSTRUCTOR = directBufferConstructor; ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField); boolean unaligned; Object maybeUnaligned = AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { try { Class<?> bitsClass = Class.forName("java.nio.Bits", false, PlatformDependent.getSystemClassLoader()); Method unalignedMethod = bitsClass.getDeclaredMethod("unaligned"); unalignedMethod.setAccessible(true); return unalignedMethod.invoke(null); } catch (ClassNotFoundException e) { return e; } catch (NoSuchMethodException e) { return e; } catch (InvocationTargetException e) { return e; } catch (IllegalAccessException e) { return e; } catch (SecurityException e) { return e; } } }); if (maybeUnaligned instanceof Boolean) { unaligned = (Boolean) maybeUnaligned; logger.debug("java.nio.Bits.unaligned: available, {}", unaligned); } else { String arch = SystemPropertyUtil.get("os.arch", ""); //noinspection DynamicRegexReplaceableByCompiledPattern unaligned = arch.matches("^(i[3-6]86|x86(_64)?|x64|amd64)$"); Exception e = (Exception) maybeUnaligned; logger.debug("java.nio.Bits.unaligned: unavailable, " + unaligned, e); } UNALIGNED = unaligned; BYTE_ARRAY_BASE_OFFSET = arrayBaseOffset(); } logger.debug("java.nio.DirectByteBuffer.<init>(long, int): {}", DIRECT_BUFFER_CONSTRUCTOR != null ? "available" : "unavailable"); freeDirectBuffer(direct); } static boolean isUnaligned() { return UNALIGNED; } static boolean hasUnsafe() { return UNSAFE != null; } static void throwException(Throwable cause) { // JVM has been observed to crash when passing a null argument. See https://github.com/netty/netty/issues/4131. UNSAFE.throwException(checkNotNull(cause, "cause")); } static boolean hasDirectBufferNoCleanerConstructor() { return DIRECT_BUFFER_CONSTRUCTOR != null; } static ByteBuffer reallocateDirectNoCleaner(ByteBuffer buffer, int capacity) { return newDirectBuffer(UNSAFE.reallocateMemory(directBufferAddress(buffer), capacity), capacity); } static ByteBuffer allocateDirectNoCleaner(int capacity) { return newDirectBuffer(UNSAFE.allocateMemory(capacity), capacity); } static ByteBuffer newDirectBuffer(long address, int capacity) { ObjectUtil.checkPositiveOrZero(address, "address"); ObjectUtil.checkPositiveOrZero(capacity, "capacity"); try { return (ByteBuffer) DIRECT_BUFFER_CONSTRUCTOR.newInstance(address, capacity); } catch (Throwable cause) { // Not expected to ever throw! if (cause instanceof Error) { throw (Error) cause; } throw new Error(cause); } } static void freeDirectBuffer(ByteBuffer buffer) { // Delegate to other class to not break on android // See https://github.com/netty/netty/issues/2604 Cleaner0.freeDirectBuffer(buffer); } static long directBufferAddress(ByteBuffer buffer) { return getLong(buffer, ADDRESS_FIELD_OFFSET); } static long arrayBaseOffset() { return UNSAFE.arrayBaseOffset(byte[].class); } static Object getObject(Object object, long fieldOffset) { return UNSAFE.getObject(object, fieldOffset); } static Object getObjectVolatile(Object object, long fieldOffset) { return UNSAFE.getObjectVolatile(object, fieldOffset); } static int getInt(Object object, long fieldOffset) { return UNSAFE.getInt(object, fieldOffset); } private static long getLong(Object object, long fieldOffset) { return UNSAFE.getLong(object, fieldOffset); } static long objectFieldOffset(Field field) { return UNSAFE.objectFieldOffset(field); } static byte getByte(long address) { return UNSAFE.getByte(address); } static short getShort(long address) { return UNSAFE.getShort(address); } static int getInt(long address) { return UNSAFE.getInt(address); } static long getLong(long address) { return UNSAFE.getLong(address); } static byte getByte(byte[] data, int index) { return UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index); } static short getShort(byte[] data, int index) { return UNSAFE.getShort(data, BYTE_ARRAY_BASE_OFFSET + index); } static int getInt(byte[] data, int index) { return UNSAFE.getInt(data, BYTE_ARRAY_BASE_OFFSET + index); } static long getLong(byte[] data, int index) { return UNSAFE.getLong(data, BYTE_ARRAY_BASE_OFFSET + index); } static void putOrderedObject(Object object, long address, Object value) { UNSAFE.putOrderedObject(object, address, value); } static void putByte(long address, byte value) { UNSAFE.putByte(address, value); } static void putShort(long address, short value) { UNSAFE.putShort(address, value); } static void putInt(long address, int value) { UNSAFE.putInt(address, value); } static void putLong(long address, long value) { UNSAFE.putLong(address, value); } static void putByte(byte[] data, int index, byte value) { UNSAFE.putByte(data, BYTE_ARRAY_BASE_OFFSET + index, value); } static void putShort(byte[] data, int index, short value) { UNSAFE.putShort(data, BYTE_ARRAY_BASE_OFFSET + index, value); } static void putInt(byte[] data, int index, int value) { UNSAFE.putInt(data, BYTE_ARRAY_BASE_OFFSET + index, value); } static void putLong(byte[] data, int index, long value) { UNSAFE.putLong(data, BYTE_ARRAY_BASE_OFFSET + index, value); } static void copyMemory(long srcAddr, long dstAddr, long length) { //UNSAFE.copyMemory(srcAddr, dstAddr, length); while (length > 0) { long size = Math.min(length, UNSAFE_COPY_THRESHOLD); UNSAFE.copyMemory(srcAddr, dstAddr, size); length -= size; srcAddr += size; dstAddr += size; } } static void copyMemory(Object src, long srcOffset, Object dst, long dstOffset, long length) { //UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, length); while (length > 0) { long size = Math.min(length, UNSAFE_COPY_THRESHOLD); UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, size); length -= size; srcOffset += size; dstOffset += size; } } static void setMemory(long address, long bytes, byte value) { UNSAFE.setMemory(address, bytes, value); } static void setMemory(Object o, long offset, long bytes, byte value) { UNSAFE.setMemory(o, offset, bytes, value); } static <U, W> AtomicReferenceFieldUpdater<U, W> newAtomicReferenceFieldUpdater( Class<? super U> tclass, String fieldName) throws Exception { return new UnsafeAtomicReferenceFieldUpdater<U, W>(UNSAFE, tclass, fieldName); } static <T> AtomicIntegerFieldUpdater<T> newAtomicIntegerFieldUpdater( Class<? super T> tclass, String fieldName) throws Exception { return new UnsafeAtomicIntegerFieldUpdater<T>(UNSAFE, tclass, fieldName); } static <T> AtomicLongFieldUpdater<T> newAtomicLongFieldUpdater( Class<? super T> tclass, String fieldName) throws Exception { return new UnsafeAtomicLongFieldUpdater<T>(UNSAFE, tclass, fieldName); } static ClassLoader getClassLoader(final Class<?> clazz) { if (System.getSecurityManager() == null) { return clazz.getClassLoader(); } else { return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { @Override public ClassLoader run() { return clazz.getClassLoader(); } }); } } static ClassLoader getContextClassLoader() { if (System.getSecurityManager() == null) { return Thread.currentThread().getContextClassLoader(); } else { return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { @Override public ClassLoader run() { return Thread.currentThread().getContextClassLoader(); } }); } } static ClassLoader getSystemClassLoader() { if (System.getSecurityManager() == null) { return ClassLoader.getSystemClassLoader(); } else { return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { @Override public ClassLoader run() { return ClassLoader.getSystemClassLoader(); } }); } } static int addressSize() { return UNSAFE.addressSize(); } static long allocateMemory(long size) { return UNSAFE.allocateMemory(size); } static void freeMemory(long address) { UNSAFE.freeMemory(address); } private PlatformDependent0() { } }