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> {
|
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) {
|
DirectArena(PooledByteBufAllocator parent, int pageSize, int maxOrder, int pageShifts, int chunkSize) {
|
||||||
super(parent, pageSize, maxOrder, pageShifts, chunkSize);
|
super(parent, pageSize, maxOrder, pageShifts, chunkSize);
|
||||||
@ -378,7 +378,7 @@ abstract class PoolArena<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PooledByteBuf<ByteBuffer> newByteBuf(int maxCapacity) {
|
protected PooledByteBuf<ByteBuffer> newByteBuf(int maxCapacity) {
|
||||||
if (USE_UNSAFE_DIRECTBUF) {
|
if (HAS_UNSAFE) {
|
||||||
return new PooledUnsafeDirectByteBuf(maxCapacity);
|
return new PooledUnsafeDirectByteBuf(maxCapacity);
|
||||||
} else {
|
} else {
|
||||||
return new PooledDirectByteBuf(maxCapacity);
|
return new PooledDirectByteBuf(maxCapacity);
|
||||||
@ -391,7 +391,7 @@ abstract class PoolArena<T> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (USE_UNSAFE_DIRECTBUF) {
|
if (HAS_UNSAFE) {
|
||||||
PlatformDependent.copyMemory(
|
PlatformDependent.copyMemory(
|
||||||
PlatformDependent.directBufferAddress(src) + srcOffset,
|
PlatformDependent.directBufferAddress(src) + srcOffset,
|
||||||
PlatformDependent.directBufferAddress(dst) + dstOffset, length);
|
PlatformDependent.directBufferAddress(dst) + dstOffset, length);
|
||||||
|
@ -41,10 +41,9 @@ public final class UnpooledByteBufAllocator extends AbstractByteBufAllocator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBuf ioBuffer() {
|
public ByteBuf ioBuffer() {
|
||||||
if (PlatformDependent.canFreeDirectBuffer()) {
|
if (PlatformDependent.hasUnsafe()) {
|
||||||
return directBuffer(0);
|
return directBuffer(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return heapBuffer(0);
|
return heapBuffer(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,8 +39,6 @@ import java.util.regex.Pattern;
|
|||||||
*/
|
*/
|
||||||
public final class PlatformDependent {
|
public final class PlatformDependent {
|
||||||
|
|
||||||
private static final ClassLoader LOADER = ClassLoader.getSystemClassLoader();
|
|
||||||
|
|
||||||
private static final boolean IS_ANDROID = isAndroid0();
|
private static final boolean IS_ANDROID = isAndroid0();
|
||||||
private static final boolean IS_WINDOWS = isWindows0();
|
private static final boolean IS_WINDOWS = isWindows0();
|
||||||
private static final boolean IS_ROOT = isRoot0();
|
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 HAS_UNSAFE = hasUnsafe0();
|
||||||
private static final boolean CAN_USE_CHM_V8 = HAS_UNSAFE && JAVA_VERSION < 8;
|
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 long ARRAY_BASE_OFFSET = arrayBaseOffset0();
|
||||||
|
|
||||||
private static final boolean HAS_JAVASSIST = hasJavassist0();
|
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() {
|
public static boolean hasUnsafe() {
|
||||||
return HAS_UNSAFE;
|
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.
|
* 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.
|
* the current platform does not support this operation or the specified buffer is not a direct buffer.
|
||||||
*/
|
*/
|
||||||
public static void freeDirectBuffer(ByteBuffer buffer) {
|
public static void freeDirectBuffer(ByteBuffer buffer) {
|
||||||
if (canFreeDirectBuffer() && buffer.isDirect()) {
|
if (buffer.isDirect()) {
|
||||||
PlatformDependent0.freeDirectBuffer(buffer);
|
PlatformDependent0.freeDirectBuffer(buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static long directBufferAddress(ByteBuffer buffer) {
|
||||||
|
return PlatformDependent0.directBufferAddress(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
public static Object getObject(Object object, long fieldOffset) {
|
public static Object getObject(Object object, long fieldOffset) {
|
||||||
return PlatformDependent0.getObject(object, fieldOffset);
|
return PlatformDependent0.getObject(object, fieldOffset);
|
||||||
}
|
}
|
||||||
@ -247,7 +236,7 @@ public final class PlatformDependent {
|
|||||||
private static boolean isAndroid0() {
|
private static boolean isAndroid0() {
|
||||||
boolean android;
|
boolean android;
|
||||||
try {
|
try {
|
||||||
Class.forName("android.app.Application", false, LOADER);
|
Class.forName("android.app.Application", false, ClassLoader.getSystemClassLoader());
|
||||||
android = true;
|
android = true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
android = false;
|
android = false;
|
||||||
@ -341,24 +330,13 @@ public final class PlatformDependent {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
return PlatformDependent0.hasUnsafe();
|
return PlatformDependent0.hasUnsafe();
|
||||||
}
|
} catch (Throwable t) {
|
||||||
|
|
||||||
private static boolean canFreeDirectBuffer0() {
|
|
||||||
if (isAndroid()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformDependent0.canFreeDirectBuffer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean unsafeHasCopyMethods0() {
|
|
||||||
if (!hasUnsafe()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return PlatformDependent0.hasCopyMethods();
|
|
||||||
}
|
|
||||||
private static long arrayBaseOffset0() {
|
private static long arrayBaseOffset0() {
|
||||||
if (!hasUnsafe()) {
|
if (!hasUnsafe()) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -31,31 +31,15 @@ final class PlatformDependent0 {
|
|||||||
private static final Unsafe UNSAFE;
|
private static final Unsafe UNSAFE;
|
||||||
private static final long CLEANER_FIELD_OFFSET;
|
private static final long CLEANER_FIELD_OFFSET;
|
||||||
private static final long ADDRESS_FIELD_OFFSET;
|
private static final long ADDRESS_FIELD_OFFSET;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code true} if and only if the platform supports unaligned access.
|
* {@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>
|
* @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 UNALIGNED;
|
||||||
private static final boolean HAS_COPY_METHODS;
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
Unsafe unsafe;
|
|
||||||
try {
|
|
||||||
Field singleoneInstanceField = Unsafe.class.getDeclaredField("theUnsafe");
|
|
||||||
singleoneInstanceField.setAccessible(true);
|
|
||||||
unsafe = (Unsafe) singleoneInstanceField.get(null);
|
|
||||||
} catch (Throwable cause) {
|
|
||||||
unsafe = null;
|
|
||||||
}
|
|
||||||
UNSAFE = unsafe;
|
|
||||||
|
|
||||||
if (unsafe == null) {
|
|
||||||
CLEANER_FIELD_OFFSET = -1;
|
|
||||||
ADDRESS_FIELD_OFFSET = -1;
|
|
||||||
UNALIGNED = false;
|
|
||||||
HAS_COPY_METHODS = false;
|
|
||||||
} else {
|
|
||||||
ByteBuffer direct = ByteBuffer.allocateDirect(1);
|
ByteBuffer direct = ByteBuffer.allocateDirect(1);
|
||||||
Field cleanerField;
|
Field cleanerField;
|
||||||
try {
|
try {
|
||||||
@ -66,7 +50,54 @@ final class PlatformDependent0 {
|
|||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
cleanerField = null;
|
cleanerField = null;
|
||||||
}
|
}
|
||||||
CLEANER_FIELD_OFFSET = cleanerField != null? objectFieldOffset(cleanerField) : -1;
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (unsafe == null) {
|
||||||
|
CLEANER_FIELD_OFFSET = -1;
|
||||||
|
ADDRESS_FIELD_OFFSET = -1;
|
||||||
|
UNALIGNED = false;
|
||||||
|
} else {
|
||||||
|
ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField);
|
||||||
|
CLEANER_FIELD_OFFSET = objectFieldOffset(cleanerField);
|
||||||
|
|
||||||
boolean unaligned;
|
boolean unaligned;
|
||||||
try {
|
try {
|
||||||
@ -75,44 +106,12 @@ final class PlatformDependent0 {
|
|||||||
unalignedMethod.setAccessible(true);
|
unalignedMethod.setAccessible(true);
|
||||||
unaligned = Boolean.TRUE.equals(unalignedMethod.invoke(null));
|
unaligned = Boolean.TRUE.equals(unalignedMethod.invoke(null));
|
||||||
} catch (Throwable t) {
|
} 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;
|
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;
|
return UNSAFE != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean canFreeDirectBuffer() {
|
static ByteBuffer newDirectBuffer(int capacity) {
|
||||||
return ADDRESS_FIELD_OFFSET >= 0;
|
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 long directBufferAddress(ByteBuffer buffer) {
|
|
||||||
return getLong(buffer, ADDRESS_FIELD_OFFSET);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void freeDirectBuffer(ByteBuffer buffer) {
|
static void freeDirectBuffer(ByteBuffer buffer) {
|
||||||
Cleaner cleaner;
|
Cleaner cleaner;
|
||||||
try {
|
try {
|
||||||
cleaner = (Cleaner) getObject(buffer, CLEANER_FIELD_OFFSET);
|
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();
|
cleaner.clean();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
// Nothing we can do here.
|
// Nothing we can do here.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean hasCopyMethods() {
|
static long directBufferAddress(ByteBuffer buffer) {
|
||||||
return HAS_COPY_METHODS;
|
return getLong(buffer, ADDRESS_FIELD_OFFSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
static long arrayBaseOffset() {
|
static long arrayBaseOffset() {
|
||||||
|
Loading…
Reference in New Issue
Block a user