diff options
author | Sven Gothel <[email protected]> | 2019-08-19 02:58:22 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2019-08-19 02:58:22 +0200 |
commit | 6603026f1bfec02e3486c52270a09a355a1bf914 (patch) | |
tree | 3017ab1e5060867ce5b7c816fc7f6621358df29e | |
parent | 5f4292bb8a310489e05a4f3545ae081310a93f72 (diff) |
Bug 1363: Java 11: Resolve Buffers.Cleaner implementation
As of Java9, sun.misc.Cleaner has moved to jdk.internal.ref.Cleaner.
However, access has been made (under the table) via sun.misc.Unsafe,
which we are using for now as we cannot set the jdk.internal.ref.Cleaner method accessible.
In this regard, we had to change our Cleaner.clean(..) method using a ByteBuffer
instead of a Buffer object paramter.
All tests have passed, no more illegal access case running on Java11 has been exposed.
-rw-r--r-- | src/java/com/jogamp/common/nio/Buffers.java | 54 | ||||
-rw-r--r-- | src/junit/com/jogamp/common/nio/TestBuffers.java (renamed from src/junit/com/jogamp/common/nio/BuffersTest.java) | 10 | ||||
-rw-r--r-- | src/junit/com/jogamp/common/nio/TestByteBufferCopyStream.java | 11 | ||||
-rw-r--r-- | src/junit/com/jogamp/common/nio/TestCachedBufferFactory.java (renamed from src/junit/com/jogamp/common/nio/CachedBufferFactoryTest.java) | 6 |
4 files changed, 68 insertions, 13 deletions
diff --git a/src/java/com/jogamp/common/nio/Buffers.java b/src/java/com/jogamp/common/nio/Buffers.java index fb23627..5125ee8 100644 --- a/src/java/com/jogamp/common/nio/Buffers.java +++ b/src/java/com/jogamp/common/nio/Buffers.java @@ -39,6 +39,7 @@ */ package com.jogamp.common.nio; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.Buffer; import java.nio.ByteBuffer; @@ -56,6 +57,7 @@ import com.jogamp.common.util.ReflectionUtil; import com.jogamp.common.util.ValueConv; import jogamp.common.Debug; +import jogamp.common.os.PlatformPropsImpl; /** * Utility methods allowing easy {@link java.nio.Buffer} manipulations. @@ -1165,22 +1167,42 @@ public class Buffers { * Access to NIO {@link sun.misc.Cleaner}, allowing caller to deterministically clean a given {@link sun.nio.ch.DirectBuffer}. */ public static class Cleaner { + private static final Object theUnsafe; private static final Method mbbCleaner; private static final Method cClean; private static final boolean hasCleaner; /** OK to be lazy on thread synchronization, just for early out **/ private static volatile boolean cleanerError; static { + final Object[] _theUnsafe = { null }; final Method[] _mbbCleaner = { null }; final Method[] _cClean = { null }; if( AccessController.doPrivileged(new PrivilegedAction<Boolean>() { @Override public Boolean run() { try { - _mbbCleaner[0] = ReflectionUtil.getMethod("sun.nio.ch.DirectBuffer", "cleaner", null, Buffers.class.getClassLoader()); - _mbbCleaner[0].setAccessible(true); - _cClean[0] = Class.forName("sun.misc.Cleaner").getMethod("clean"); - _cClean[0].setAccessible(true); + if(PlatformPropsImpl.JAVA_9) { + // Using: sun.misc.Unsafe { public void invokeCleaner(java.nio.ByteBuffer directBuffer); } + _mbbCleaner[0] = null; + final Class<?> unsafeClass = Class.forName("sun.misc.Unsafe"); + { + final Field f = unsafeClass.getDeclaredField("theUnsafe"); + f.setAccessible(true); + _theUnsafe[0] = f.get(null); + } + _cClean[0] = unsafeClass.getMethod("invokeCleaner", java.nio.ByteBuffer.class); + _cClean[0].setAccessible(true); + } else { + _mbbCleaner[0] = ReflectionUtil.getMethod("sun.nio.ch.DirectBuffer", "cleaner", null, Buffers.class.getClassLoader()); + _mbbCleaner[0].setAccessible(true); + final Class<?> cleanerType = _mbbCleaner[0].getReturnType(); + // Java >= 9: jdk.internal.ref.Cleaner (NOT accessible!) + // "Unable to make public void jdk.internal.ref.Cleaner.clean() accessible: + // module java.base does not "exports jdk.internal.ref" to unnamed module" + // Java <= 8: sun.misc.Cleaner OK + _cClean[0] = cleanerType.getMethod("clean"); + _cClean[0].setAccessible(true); + } return Boolean.TRUE; } catch(final Throwable t) { if( DEBUG ) { @@ -1189,27 +1211,43 @@ public class Buffers { } return Boolean.FALSE; } } } ).booleanValue() ) { + theUnsafe = _theUnsafe[0]; mbbCleaner = _mbbCleaner[0]; cClean = _cClean[0]; - hasCleaner = null != mbbCleaner && null != cClean; + hasCleaner = PlatformPropsImpl.JAVA_9 ? null != theUnsafe && null != cClean : null != mbbCleaner && null != cClean; } else { + theUnsafe = null; mbbCleaner = null; cClean = null; hasCleaner = false; } cleanerError = !hasCleaner; + if( DEBUG ) { + System.err.print("Buffers.Cleaner.init: hasCleaner: "+hasCleaner+", cleanerError "+cleanerError); + if( null != mbbCleaner ) { + System.err.print(", using Cleaner class: "+mbbCleaner.getReturnType().getName()); + } + if( null != theUnsafe ) { + System.err.print(", using sun.misc.Unsafe::invokeCleaner(..);"); + } + System.err.println(); + } } /** * If {@code b} is an direct NIO buffer, i.e {@link sun.nio.ch.DirectBuffer}, * calls it's {@link sun.misc.Cleaner} instance {@code clean()} method. * @return {@code true} if successful, otherwise {@code false}. */ - public static boolean clean(final Buffer b) { - if( !hasCleaner || cleanerError || !b.isDirect() ) { + public static boolean clean(final ByteBuffer bb) { + if( !hasCleaner || cleanerError || !bb.isDirect() ) { return false; } try { - cClean.invoke(mbbCleaner.invoke(b)); + if( PlatformPropsImpl.JAVA_9 ) { + cClean.invoke(theUnsafe, bb); + } else { + cClean.invoke(mbbCleaner.invoke(bb)); + } return true; } catch(final Throwable t) { cleanerError = true; diff --git a/src/junit/com/jogamp/common/nio/BuffersTest.java b/src/junit/com/jogamp/common/nio/TestBuffers.java index c267100..89b5671 100644 --- a/src/junit/com/jogamp/common/nio/BuffersTest.java +++ b/src/junit/com/jogamp/common/nio/TestBuffers.java @@ -53,7 +53,7 @@ import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class BuffersTest extends SingletonJunitCase { +public class TestBuffers extends SingletonJunitCase { @Test public void test01PositionLimitCapacityAfterArrayAllocation() { @@ -156,8 +156,14 @@ public class BuffersTest extends SingletonJunitCase { assertEquals(42, onetwothree.get(2)); } + @Test + public void test20Cleaner() { + final ByteBuffer byteBuffer = Buffers.newDirectByteBuffer(1024); + Buffers.Cleaner.clean(byteBuffer); + } + public static void main(final String args[]) throws IOException { - final String tstname = BuffersTest.class.getName(); + final String tstname = TestBuffers.class.getName(); org.junit.runner.JUnitCore.main(tstname); } } diff --git a/src/junit/com/jogamp/common/nio/TestByteBufferCopyStream.java b/src/junit/com/jogamp/common/nio/TestByteBufferCopyStream.java index 9524e91..aef0d3e 100644 --- a/src/junit/com/jogamp/common/nio/TestByteBufferCopyStream.java +++ b/src/junit/com/jogamp/common/nio/TestByteBufferCopyStream.java @@ -52,6 +52,8 @@ public class TestByteBufferCopyStream extends SingletonJunitCase { final MappedByteBufferInputStream.CacheMode srcCacheMode, final int srcSliceShift, final String dstFileName, final MappedByteBufferInputStream.CacheMode dstCacheMode, final int dstSliceShift ) throws IOException { + System.err.println("Test: source[CacheMode "+srcCacheMode+", SliceShift "+srcSliceShift+"]"); + System.err.println(" destin[CacheMode "+dstCacheMode+", SliceShift "+dstSliceShift+"]"); final Runtime runtime = Runtime.getRuntime(); final long[] usedMem0 = { 0 }; final long[] freeMem0 = { 0 }; @@ -60,6 +62,7 @@ public class TestByteBufferCopyStream extends SingletonJunitCase { final String prefix = "test "+String.format(TestByteBufferInputStream.PrintPrecision+" MiB", size/TestByteBufferInputStream.MIB); TestByteBufferInputStream.dumpMem(prefix+" before", runtime, -1, -1, usedMem0, freeMem0 ); + final long t0 = Platform.currentTimeMillis(); final File srcFile = new File(srcFileName); srcFile.delete(); srcFile.createNewFile(); @@ -72,6 +75,7 @@ public class TestByteBufferCopyStream extends SingletonJunitCase { _input.close(); input = new RandomAccessFile(srcFile, "r"); } + final long t1 = Platform.currentTimeMillis(); final MappedByteBufferInputStream mis = new MappedByteBufferInputStream(input.getChannel(), FileChannel.MapMode.READ_ONLY, srcCacheMode, @@ -131,12 +135,19 @@ public class TestByteBufferCopyStream extends SingletonJunitCase { output.close(); srcFile.delete(); dstFile.delete(); + final long t5 = Platform.currentTimeMillis(); TestByteBufferInputStream.dumpMem(prefix+" after ", runtime, usedMem0[0], freeMem0[0], usedMem1, freeMem1 ); System.gc(); + final long t6 = Platform.currentTimeMillis(); try { Thread.sleep(500); } catch (final InterruptedException e) { } TestByteBufferInputStream.dumpMem(prefix+" gc'ed ", runtime, usedMem0[0], freeMem0[0], usedMem1, freeMem1 ); + System.err.println("Performance Stats: "); + System.err.printf("- File-Create %6d ms\n", t1-t0); + System.err.printf("- File-Copy %6d ms\n", t5-t1); + System.err.printf("- GC %6d ms\n", t6-t5); + System.err.printf("- Total %6d ms\n", t6-t0); } if( null != ioe || null != oome ) { if( null != oome ) { diff --git a/src/junit/com/jogamp/common/nio/CachedBufferFactoryTest.java b/src/junit/com/jogamp/common/nio/TestCachedBufferFactory.java index 6b9409f..6ed1b91 100644 --- a/src/junit/com/jogamp/common/nio/CachedBufferFactoryTest.java +++ b/src/junit/com/jogamp/common/nio/TestCachedBufferFactory.java @@ -55,7 +55,7 @@ import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class CachedBufferFactoryTest extends SingletonJunitCase { +public class TestCachedBufferFactory extends SingletonJunitCase { private final int BUFFERCOUNT = 120; @@ -210,7 +210,7 @@ public class CachedBufferFactoryTest extends SingletonJunitCase { public static void main(final String[] args) { - CachedBufferFactoryTest test = new CachedBufferFactoryTest(); + TestCachedBufferFactory test = new TestCachedBufferFactory(); out.print("warmup..."); Object obj = null; @@ -221,7 +221,7 @@ public class CachedBufferFactoryTest extends SingletonJunitCase { } out.println("done"); - test = new CachedBufferFactoryTest(); + test = new TestCachedBufferFactory(); gc(); for (int i = 0; i < 10; i++) { |