Skip to content

Commit

Permalink
Update unmap support to latest Java 9
Browse files Browse the repository at this point in the history
jdk9+148 changed visibility rules in a way that breaks the previous
mechanism for invoking the mapped bytebuffer cleaner.

In later versions, a new method (Unsafe.invokeCleaner) is available
as a workaround (https://bugs.openjdk.java.net/browse/JDK-8171377)
  • Loading branch information
martint committed Jul 27, 2017
1 parent 14d4458 commit 39b6e0c
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 19 deletions.
1 change: 1 addition & 0 deletions leveldb/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@
<option>-dontwarn org.xerial.snappy.SnappyLoader</option>
<option>-dontwarn org.xerial.snappy.SnappyBundleActivator</option>
<option>-dontwarn org.iq80.snappy.HadoopSnappyCodec**</option>
<option>-dontwarn org.iq80.leveldb.util.ByteBufferSupport</option>
<option>-dontoptimize</option>
</options>
</configuration>
Expand Down
54 changes: 35 additions & 19 deletions leveldb/src/main/java/org/iq80/leveldb/util/ByteBufferSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,35 +19,52 @@

import com.google.common.base.Throwables;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;

public final class ByteBufferSupport
{
private static final Method getCleaner;
private static final Method clean;
private static final MethodHandle INVOKE_CLEANER;

static {
MethodHandle invoker;
try {
getCleaner = Class.forName("java.nio.DirectByteBuffer").getDeclaredMethod("cleaner");
getCleaner.setAccessible(true);
}
catch (ReflectiveOperationException e) {
throw new AssertionError(e);
// Java 9 added an invokeCleaner method to Unsafe to work around
// module visibility issues for code that used to rely on DirectByteBuffer's cleaner()
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
invoker = MethodHandles.lookup()
.findVirtual(unsafeClass, "invokeCleaner", MethodType.methodType(void.class, ByteBuffer.class))
.bindTo(theUnsafe.get(null));
}
catch (Exception e) {
// fall back to pre-java 9 compatible behavior
try {
Class<?> directByteBufferClass = Class.forName("java.nio.DirectByteBuffer");
Class<?> cleanerClass = Class.forName("sun.misc.Cleaner");

try {
Class<?> returnType = getCleaner.getReturnType();
if (Runnable.class.isAssignableFrom(returnType)) {
clean = Runnable.class.getMethod("run");
Method cleanerMethod = directByteBufferClass.getDeclaredMethod("cleaner");
cleanerMethod.setAccessible(true);
MethodHandle getCleaner = MethodHandles.lookup().unreflect(cleanerMethod);

Method cleanMethod = cleanerClass.getDeclaredMethod("clean");
cleanerMethod.setAccessible(true);
MethodHandle clean = MethodHandles.lookup().unreflect(cleanMethod);

clean = MethodHandles.dropArguments(clean, 1, directByteBufferClass);
invoker = MethodHandles.foldArguments(clean, getCleaner);
}
else {
clean = returnType.getMethod("clean");
catch (Exception e1) {
throw new AssertionError(e1);
}
}
catch (NoSuchMethodException e) {
throw new AssertionError(e);
}
INVOKE_CLEANER = invoker;
}

private ByteBufferSupport()
Expand All @@ -57,10 +74,9 @@ private ByteBufferSupport()
public static void unmap(MappedByteBuffer buffer)
{
try {
Object cleaner = getCleaner.invoke(buffer);
clean.invoke(cleaner);
INVOKE_CLEANER.invoke(buffer);
}
catch (Exception ignored) {
catch (Throwable ignored) {
throw Throwables.propagate(ignored);
}
}
Expand Down

0 comments on commit 39b6e0c

Please sign in to comment.