Make hasUnsafe() return true only when all necessary low level operations are available for reliable direct buffer access

This commit is contained in:
Trustin Lee 2013-03-05 15:25:25 +09:00
parent 256c931db2
commit 307e6c47d8
4 changed files with 85 additions and 105 deletions

View File

@ -354,7 +354,7 @@ abstract class PoolArena<T> {
static final class DirectArena extends PoolArena<ByteBuffer> {
private static final boolean USE_UNSAFE_DIRECTBUF = PlatformDependent.unsafeHasCopyMethods();
private static final boolean HAS_UNSAFE = PlatformDependent.hasUnsafe();
DirectArena(PooledByteBufAllocator parent, int pageSize, int maxOrder, int pageShifts, int chunkSize) {
super(parent, pageSize, maxOrder, pageShifts, chunkSize);
@ -378,7 +378,7 @@ abstract class PoolArena<T> {
@Override
protected PooledByteBuf<ByteBuffer> newByteBuf(int maxCapacity) {
if (USE_UNSAFE_DIRECTBUF) {
if (HAS_UNSAFE) {
return new PooledUnsafeDirectByteBuf(maxCapacity);
} else {
return new PooledDirectByteBuf(maxCapacity);
@ -391,7 +391,7 @@ abstract class PoolArena<T> {
return;
}
if (USE_UNSAFE_DIRECTBUF) {
if (HAS_UNSAFE) {
PlatformDependent.copyMemory(
PlatformDependent.directBufferAddress(src) + srcOffset,
PlatformDependent.directBufferAddress(dst) + dstOffset, length);

View File

@ -41,10 +41,9 @@ public final class UnpooledByteBufAllocator extends AbstractByteBufAllocator {
@Override
public ByteBuf ioBuffer() {
if (PlatformDependent.canFreeDirectBuffer()) {
if (PlatformDependent.hasUnsafe()) {
return directBuffer(0);
}
return heapBuffer(0);
}
}

View File

@ -39,8 +39,6 @@ import java.util.regex.Pattern;
*/
public final class PlatformDependent {
private static final ClassLoader LOADER = ClassLoader.getSystemClassLoader();
private static final boolean IS_ANDROID = isAndroid0();
private static final boolean IS_WINDOWS = isWindows0();
private static final boolean IS_ROOT = isRoot0();
@ -51,8 +49,6 @@ public final class PlatformDependent {
private static final boolean HAS_UNSAFE = hasUnsafe0();
private static final boolean CAN_USE_CHM_V8 = HAS_UNSAFE && JAVA_VERSION < 8;
private static final boolean CAN_FREE_DIRECT_BUFFER = canFreeDirectBuffer0();
private static final boolean UNSAFE_HASE_COPY_METHODS = unsafeHasCopyMethods0();
private static final long ARRAY_BASE_OFFSET = arrayBaseOffset0();
private static final boolean HAS_JAVASSIST = hasJavassist0();
@ -94,27 +90,13 @@ public final class PlatformDependent {
}
/**
* Return {@code true} if {@code sun.misc.Unsafe} was found on the classpath and can be used.
* Return {@code true} if {@code sun.misc.Unsafe} was found on the classpath and can be used for acclerated
* direct memory access.
*/
public static boolean hasUnsafe() {
return HAS_UNSAFE;
}
/**
* Return {@code true} if direct buffers can be freed using an optimized way and so memory footprint will be very
* small.
*/
public static boolean canFreeDirectBuffer() {
return CAN_FREE_DIRECT_BUFFER;
}
/**
* Returns {@code true} if unsafe has all needed copy methods which is not the case on latest openjdk6 atm.
*/
public static boolean unsafeHasCopyMethods() {
return UNSAFE_HASE_COPY_METHODS;
}
/**
* Returns {@code true} if and only if Javassist is available.
*/
@ -178,8 +160,11 @@ public final class PlatformDependent {
}
}
public static long directBufferAddress(ByteBuffer buffer) {
return PlatformDependent0.directBufferAddress(buffer);
/**
* Try to allocate a new direct {@link ByteBuffer} using the best available allocator.
*/
public static ByteBuffer newDirectBuffer(int capacity) {
return PlatformDependent0.newDirectBuffer(capacity);
}
/**
@ -187,11 +172,15 @@ public final class PlatformDependent {
* the current platform does not support this operation or the specified buffer is not a direct buffer.
*/
public static void freeDirectBuffer(ByteBuffer buffer) {
if (canFreeDirectBuffer() && buffer.isDirect()) {
if (buffer.isDirect()) {
PlatformDependent0.freeDirectBuffer(buffer);
}
}
public static long directBufferAddress(ByteBuffer buffer) {
return PlatformDependent0.directBufferAddress(buffer);
}
public static Object getObject(Object object, long fieldOffset) {
return PlatformDependent0.getObject(object, fieldOffset);
}
@ -247,7 +236,7 @@ public final class PlatformDependent {
private static boolean isAndroid0() {
boolean android;
try {
Class.forName("android.app.Application", false, LOADER);
Class.forName("android.app.Application", false, ClassLoader.getSystemClassLoader());
android = true;
} catch (Exception e) {
android = false;
@ -341,24 +330,13 @@ public final class PlatformDependent {
return false;
}
return PlatformDependent0.hasUnsafe();
}
private static boolean canFreeDirectBuffer0() {
if (isAndroid()) {
try {
return PlatformDependent0.hasUnsafe();
} catch (Throwable t) {
return false;
}
return PlatformDependent0.canFreeDirectBuffer();
}
private static boolean unsafeHasCopyMethods0() {
if (!hasUnsafe()) {
return false;
}
return PlatformDependent0.hasCopyMethods();
}
private static long arrayBaseOffset0() {
if (!hasUnsafe()) {
return -1;

View File

@ -31,21 +31,62 @@ final class PlatformDependent0 {
private static final Unsafe UNSAFE;
private static final long CLEANER_FIELD_OFFSET;
private static final long ADDRESS_FIELD_OFFSET;
/**
* {@code true} if and only if the platform supports unaligned access.
*
* @see <a href="http://en.wikipedia.org/wiki/Segmentation_fault#Bus_error">Wikipedia on segfault</a>
*/
private static final boolean UNALIGNED;
private static final boolean HAS_COPY_METHODS;
static {
Unsafe unsafe;
ByteBuffer direct = ByteBuffer.allocateDirect(1);
Field cleanerField;
try {
Field singleoneInstanceField = Unsafe.class.getDeclaredField("theUnsafe");
singleoneInstanceField.setAccessible(true);
unsafe = (Unsafe) singleoneInstanceField.get(null);
} catch (Throwable cause) {
cleanerField = direct.getClass().getDeclaredField("cleaner");
cleanerField.setAccessible(true);
Cleaner cleaner = (Cleaner) cleanerField.get(direct);
cleaner.clean();
} catch (Throwable t) {
cleanerField = null;
}
Field addressField;
try {
addressField = Buffer.class.getDeclaredField("address");
addressField.setAccessible(true);
if (addressField.getLong(ByteBuffer.allocate(1)) != 0) {
addressField = null;
} else {
direct = ByteBuffer.allocateDirect(1);
if (addressField.getLong(direct) == 0) {
addressField = null;
}
Cleaner cleaner = (Cleaner) cleanerField.get(direct);
cleaner.clean();
}
} catch (Throwable t) {
addressField = null;
}
Unsafe unsafe;
if (addressField != null && cleanerField != null) {
try {
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
unsafe = (Unsafe) unsafeField.get(null);
// 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
unsafe.getClass().getDeclaredMethod(
"copyMemory", new Class[] { Object.class, long.class, Object.class, long.class, long.class });
} catch (Throwable cause) {
unsafe = null;
}
} 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;
@ -54,19 +95,9 @@ final class PlatformDependent0 {
CLEANER_FIELD_OFFSET = -1;
ADDRESS_FIELD_OFFSET = -1;
UNALIGNED = false;
HAS_COPY_METHODS = false;
} else {
ByteBuffer direct = ByteBuffer.allocateDirect(1);
Field cleanerField;
try {
cleanerField = direct.getClass().getDeclaredField("cleaner");
cleanerField.setAccessible(true);
Cleaner cleaner = (Cleaner) cleanerField.get(direct);
cleaner.clean();
} catch (Throwable t) {
cleanerField = null;
}
CLEANER_FIELD_OFFSET = cleanerField != null? objectFieldOffset(cleanerField) : -1;
ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField);
CLEANER_FIELD_OFFSET = objectFieldOffset(cleanerField);
boolean unaligned;
try {
@ -75,44 +106,12 @@ final class PlatformDependent0 {
unalignedMethod.setAccessible(true);
unaligned = Boolean.TRUE.equals(unalignedMethod.invoke(null));
} catch (Throwable t) {
unaligned = false;
// We at least know x86 and x64 support unaligned access.
String arch = SystemPropertyUtil.get("os.arch", "");
//noinspection DynamicRegexReplaceableByCompiledPattern
unaligned = arch.matches("^(i[3-6]86|x86(_64)?|x64|amd64)$");
}
if (unaligned) {
Field addressField = null;
try {
addressField = Buffer.class.getDeclaredField("address");
addressField.setAccessible(true);
if (addressField.getLong(ByteBuffer.allocate(1)) != 0) {
unaligned = false;
} else {
ByteBuffer directBuf = ByteBuffer.allocateDirect(1);
if (addressField.getLong(directBuf) == 0) {
unaligned = false;
}
freeDirectBuffer(directBuf);
}
} catch (Throwable t) {
unaligned = false;
}
ADDRESS_FIELD_OFFSET = addressField != null? objectFieldOffset(addressField) : -1;
} else {
ADDRESS_FIELD_OFFSET = -1;
}
UNALIGNED = unaligned;
boolean hasCopyMethods;
try {
// Unsafe does not shop all copy methods in latest openjdk update..
// https://github.com/netty/netty/issues/1061
// http://www.mail-archive.com/jdk6-dev@openjdk.java.net/msg00698.html
UNSAFE.getClass().getDeclaredMethod("copyMemory",
new Class[] { Object.class, long.class, Object.class, long.class, long.class });
hasCopyMethods = true;
} catch (Throwable ignore) {
hasCopyMethods = false;
}
HAS_COPY_METHODS = hasCopyMethods;
}
}
@ -120,26 +119,30 @@ final class PlatformDependent0 {
return UNSAFE != null;
}
static boolean canFreeDirectBuffer() {
return ADDRESS_FIELD_OFFSET >= 0;
}
static long directBufferAddress(ByteBuffer buffer) {
return getLong(buffer, ADDRESS_FIELD_OFFSET);
static ByteBuffer newDirectBuffer(int capacity) {
if (hasUnsafe()) {
return ByteBuffer.allocateDirect(capacity);
} else {
throw new Error("refusing to create a direct buffer without proper access to the low-level API");
}
}
static void freeDirectBuffer(ByteBuffer buffer) {
Cleaner cleaner;
try {
cleaner = (Cleaner) getObject(buffer, CLEANER_FIELD_OFFSET);
if (cleaner == null) {
throw new IllegalArgumentException(
"attempted to deallocate the buffer which was allocated via JNIEnv->NewDirectByteBuffer()");
}
cleaner.clean();
} catch (Throwable t) {
// Nothing we can do here.
}
}
static boolean hasCopyMethods() {
return HAS_COPY_METHODS;
static long directBufferAddress(ByteBuffer buffer) {
return getLong(buffer, ADDRESS_FIELD_OFFSET);
}
static long arrayBaseOffset() {