From d57d041f48ceffb73becb79d21fef68588e2729d Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 16 Feb 2017 15:36:10 +0100 Subject: [PATCH] Use Unsafe to read ByteBuffer.address field to make it work on Java9 as well. Motivation: Java9 does not allow changing access level via reflection by default. This lead to the situation that netty disabled Unsafe completely as ByteBuffer.address could not be read. Modification: Use Unsafe to read the address field as this works on all Java versions. Result: Again be able to use Unsafe optimisations when using Netty with Java9 --- .../WrappedUnpooledUnsafeByteBufTest.java | 3 +- .../util/internal/PlatformDependent0.java | 83 ++++++++++--------- 2 files changed, 44 insertions(+), 42 deletions(-) diff --git a/buffer/src/test/java/io/netty/buffer/WrappedUnpooledUnsafeByteBufTest.java b/buffer/src/test/java/io/netty/buffer/WrappedUnpooledUnsafeByteBufTest.java index 2a0242b358..4357a93ca1 100644 --- a/buffer/src/test/java/io/netty/buffer/WrappedUnpooledUnsafeByteBufTest.java +++ b/buffer/src/test/java/io/netty/buffer/WrappedUnpooledUnsafeByteBufTest.java @@ -27,7 +27,8 @@ public class WrappedUnpooledUnsafeByteBufTest extends BigEndianUnsafeDirectByteB @Before @Override public void init() { - Assume.assumeTrue("sun.misc.Unsafe not found, skip tests", PlatformDependent.hasUnsafe()); + Assume.assumeTrue("PlatformDependent.useDirectBufferNoCleaner() returned false, skip tests", + PlatformDependent.useDirectBufferNoCleaner()); super.init(); } diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java index 76f55705cf..1565123cb9 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java @@ -51,49 +51,16 @@ final class PlatformDependent0 { static { final ByteBuffer direct; - final Field addressField; + Field addressField = null; + Unsafe unsafe; if (PlatformDependent.isExplicitNoUnsafe()) { direct = null; addressField = null; + unsafe = null; } else { direct = ByteBuffer.allocateDirect(1); - // attempt to access field Buffer#address - final Object maybeAddressField = AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Object run() { - try { - final Field field = Buffer.class.getDeclaredField("address"); - Throwable cause = ReflectionUtil.trySetAccessible(field); - if (cause != null) { - return cause; - } - // 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", (Throwable) maybeAddressField); - addressField = null; - } - } - - Unsafe unsafe; - if (addressField != null) { // attempt to access field Unsafe#theUnsafe final Object maybeUnsafe = AccessController.doPrivileged(new PrivilegedAction() { @Override @@ -156,12 +123,46 @@ final class PlatformDependent0 { logger.debug("sun.misc.Unsafe.copyMemory: unavailable", (Throwable) 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; - } + if (unsafe != null) { + final Unsafe finalUnsafe = unsafe; + + // attempt to access field Buffer#address + final Object maybeAddressField = AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Object run() { + try { + final Field field = Buffer.class.getDeclaredField("address"); + // Use Unsafe to read value of the address field. This way it will not fail on JDK9+ which + // will forbid changing the access level via reflection. + final long offset = finalUnsafe.objectFieldOffset(field); + final long address = finalUnsafe.getLong(direct, offset); + + // if direct really is a direct buffer, address will be non-zero + if (address == 0) { + return null; + } + return field; + } 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", (Throwable) maybeAddressField); + + // 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) {