Make hasUnsafe() return true only when all necessary low level operations are available for reliable direct buffer access
This commit is contained in:
parent
256c931db2
commit
307e6c47d8
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
Loading…
Reference in New Issue
Block a user