diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent.java b/common/src/main/java/io/netty/util/internal/PlatformDependent.java index 686a2cf314..b16b21d42d 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent.java @@ -27,6 +27,7 @@ import org.jctools.queues.atomic.MpscAtomicArrayQueue; import org.jctools.queues.atomic.MpscLinkedAtomicQueue; import org.jctools.queues.atomic.SpscLinkedAtomicQueue; import org.jctools.util.Pow2; +import org.jctools.util.UnsafeAccess; import java.io.BufferedReader; import java.io.File; @@ -833,6 +834,50 @@ public final class PlatformDependent { return null; } + private static final class Mpsc { + private static final boolean USE_MPSC_CHUNKED_ARRAY_QUEUE; + + private Mpsc() { + } + + static { + Object unsafe = null; + if (hasUnsafe()) { + // jctools goes through its own process of initializing unsafe; of + // course, this requires permissions which might not be granted to calling code, so we + // must mark this block as privileged too + unsafe = AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Object run() { + // force JCTools to initialize unsafe + return UnsafeAccess.UNSAFE; + } + }); + } + + if (unsafe == null) { + logger.debug("org.jctools-core.MpscChunkedArrayQueue: unavailable"); + USE_MPSC_CHUNKED_ARRAY_QUEUE = false; + } else { + logger.debug("org.jctools-core.MpscChunkedArrayQueue: available"); + USE_MPSC_CHUNKED_ARRAY_QUEUE = true; + } + } + + static Queue newMpscQueue(final int maxCapacity) { + if (USE_MPSC_CHUNKED_ARRAY_QUEUE) { + // Calculate the max capacity which can not be bigger then MAX_ALLOWED_MPSC_CAPACITY. + // This is forced by the MpscChunkedArrayQueue implementation as will try to round it + // up to the next power of two and so will overflow otherwise. + final int capacity = + Math.max(Math.min(maxCapacity, MAX_ALLOWED_MPSC_CAPACITY), MIN_MAX_MPSC_CAPACITY); + return new MpscChunkedArrayQueue(MPSC_CHUNK_SIZE, capacity, true); + } else { + return new MpscLinkedAtomicQueue(); + } + } + } + /** * Create a new {@link Queue} which is safe to use for multiple producers (different threads) and a single * consumer (one thread!). @@ -845,14 +890,8 @@ public final class PlatformDependent { * Create a new {@link Queue} which is safe to use for multiple producers (different threads) and a single * consumer (one thread!). */ - public static Queue newMpscQueue(int maxCapacity) { - return hasUnsafe() ? - new MpscChunkedArrayQueue(MPSC_CHUNK_SIZE, - // Calculate the max capacity which can not be bigger then MAX_ALLOWED_MPSC_CAPACITY. - // This is forced by the MpscChunkedArrayQueue implementation as will try to round it - // up to the next power of two and so will overflow otherwise. - Math.max(Math.min(maxCapacity, MAX_ALLOWED_MPSC_CAPACITY), MIN_MAX_MPSC_CAPACITY), true) - : new MpscLinkedAtomicQueue(); + public static Queue newMpscQueue(final int maxCapacity) { + return Mpsc.newMpscQueue(maxCapacity); } /** 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 3f0cd49ca0..31e374b0c9 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java @@ -21,6 +21,7 @@ import sun.misc.Unsafe; 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; @@ -57,49 +58,98 @@ final class PlatformDependent0 { private static final boolean UNALIGNED; static { - ByteBuffer direct = ByteBuffer.allocateDirect(1); - Field addressField; - try { - addressField = Buffer.class.getDeclaredField("address"); - addressField.setAccessible(true); - if (addressField.getLong(direct) == 0) { - // A direct buffer must have non-zero address. - addressField = null; + final ByteBuffer direct = ByteBuffer.allocateDirect(1); + final Field addressField; + // 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"); + 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; + } } - } catch (Throwable t) { - // Failed to access the address field. + }); + + 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; } - logger.debug("java.nio.Buffer.address: {}", addressField != null? "available" : "unavailable"); - Unsafe unsafe; if (addressField != null) { - try { - Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); - unsafeField.setAccessible(true); - unsafe = (Unsafe) unsafeField.get(null); - logger.debug("sun.misc.Unsafe.theUnsafe: {}", unsafe != null ? "available" : "unavailable"); - - // 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 - try { - if (unsafe != null) { - unsafe.getClass().getDeclaredMethod( - "copyMemory", Object.class, long.class, Object.class, long.class, long.class); - logger.debug("sun.misc.Unsafe.copyMemory: available"); + // attempt to access field Unsafe#theUnsafe + final Object maybeUnsafe = AccessController.doPrivileged(new PrivilegedAction() { + @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; } - } catch (NoSuchMethodError t) { - logger.debug("sun.misc.Unsafe.copyMemory: unavailable"); - throw t; - } catch (NoSuchMethodException e) { - logger.debug("sun.misc.Unsafe.copyMemory: unavailable"); - throw e; } - } catch (Throwable cause) { - // Unsafe.copyMemory(Object, long, Object, long, long) unavailable. + }); + + // 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() { + @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. @@ -118,14 +168,43 @@ final class PlatformDependent0 { Constructor directBufferConstructor; long address = -1; try { - directBufferConstructor = direct.getClass().getDeclaredConstructor(long.class, int.class); - directBufferConstructor.setAccessible(true); - address = UNSAFE.allocateMemory(1); + final Object maybeDirectBufferConstructor = + AccessController.doPrivileged(new PrivilegedAction() { + @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; + } + } + }); - // Try to use the constructor now - directBufferConstructor.newInstance(address, 1); - } catch (Throwable t) { - directBufferConstructor = null; + 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); @@ -136,24 +215,45 @@ final class PlatformDependent0 { ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField); BYTE_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class); boolean unaligned; - try { - Class bitsClass = Class.forName("java.nio.Bits", false, ClassLoader.getSystemClassLoader()); - Method unalignedMethod = bitsClass.getDeclaredMethod("unaligned"); - unalignedMethod.setAccessible(true); - unaligned = Boolean.TRUE.equals(unalignedMethod.invoke(null)); - } catch (Throwable t) { - // We at least know x86 and x64 support unaligned access. + Object maybeUnaligned = AccessController.doPrivileged(new PrivilegedAction() { + @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; - logger.debug("java.nio.Bits.unaligned: {}", UNALIGNED); } logger.debug("java.nio.DirectByteBuffer.(long, int): {}", - DIRECT_BUFFER_CONSTRUCTOR != null? "available" : "unavailable"); + DIRECT_BUFFER_CONSTRUCTOR != null ? "available" : "unavailable"); freeDirectBuffer(direct); }