Merge pull request #228 from bokken/master
java 9 valid direct byte buffer release
This commit is contained in:
commit
de99182a82
|
@ -3,10 +3,22 @@
|
||||||
*/
|
*/
|
||||||
package org.xerial.snappy;
|
package org.xerial.snappy;
|
||||||
|
|
||||||
|
import static java.lang.invoke.MethodHandles.constant;
|
||||||
|
import static java.lang.invoke.MethodHandles.dropArguments;
|
||||||
|
import static java.lang.invoke.MethodHandles.filterReturnValue;
|
||||||
|
import static java.lang.invoke.MethodHandles.guardWithTest;
|
||||||
|
import static java.lang.invoke.MethodHandles.lookup;
|
||||||
|
import static java.lang.invoke.MethodType.methodType;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles.Lookup;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.ReadableByteChannel;
|
import java.nio.channels.ReadableByteChannel;
|
||||||
|
import java.security.AccessController;
|
||||||
|
import java.security.PrivilegedExceptionAction;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
@ -30,27 +42,76 @@ final class SnappyFramed
|
||||||
* Sun specific mechanisms to clean up resources associated with direct byte buffers.
|
* Sun specific mechanisms to clean up resources associated with direct byte buffers.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private static final Class<? extends ByteBuffer> SUN_DIRECT_BUFFER = (Class<? extends ByteBuffer>) lookupClassQuietly("sun.nio.ch.DirectBuffer");
|
static final Class<? extends ByteBuffer> DIRECT_BUFFER_CLAZZ = (Class<? extends ByteBuffer>) lookupClassQuietly("java.nio.DirectByteBuffer");
|
||||||
private static final Method SUN_BUFFER_CLEANER;
|
|
||||||
private static final Method SUN_CLEANER_CLEAN;
|
|
||||||
|
|
||||||
|
static final MethodHandle CLEAN_HANDLE;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
Method bufferCleaner = null;
|
// this approach is based off that used by apache lucene and documented here: https://issues.apache.org/jira/browse/LUCENE-6989
|
||||||
Method cleanerClean = null;
|
// and https://github.com/apache/lucene-solr/blob/7e03427fa14a024ce257babcb8362d2451941e21/lucene/core/src/java/org/apache/lucene/store/MMapDirectory.java
|
||||||
|
MethodHandle cleanHandle = null;
|
||||||
try {
|
try {
|
||||||
//operate under the assumption that if the sun direct buffer class exists,
|
final PrivilegedExceptionAction<MethodHandle> action = new PrivilegedExceptionAction<MethodHandle>() {
|
||||||
//all of the sun classes exist
|
|
||||||
if (SUN_DIRECT_BUFFER != null) {
|
@Override
|
||||||
bufferCleaner = SUN_DIRECT_BUFFER.getMethod("cleaner", (Class[]) null);
|
public MethodHandle run() throws Exception {
|
||||||
Class<?> cleanClazz = lookupClassQuietly("sun.misc.Cleaner");
|
MethodHandle handle = null;
|
||||||
cleanerClean = cleanClazz.getMethod("clean", (Class[]) null);
|
if (DIRECT_BUFFER_CLAZZ != null) {
|
||||||
}
|
final Lookup lookup = lookup();
|
||||||
}
|
|
||||||
catch (Throwable t) {
|
try {
|
||||||
|
// sun.misc.Unsafe unmapping (Java 9+)
|
||||||
|
final Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
|
||||||
|
// first check if Unsafe has the right method, otherwise we can give up
|
||||||
|
// without doing any security critical stuff:
|
||||||
|
final MethodHandle unmapper = lookup.findVirtual(unsafeClass, "invokeCleaner", methodType(void.class, ByteBuffer.class));
|
||||||
|
// fetch the unsafe instance and bind it to the virtual MH:
|
||||||
|
final Field f = unsafeClass.getDeclaredField("theUnsafe");
|
||||||
|
f.setAccessible(true);
|
||||||
|
final Object theUnsafe = f.get(null);
|
||||||
|
handle = unmapper.bindTo(theUnsafe);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.getLogger(SnappyFramed.class.getName()).log(Level.FINE, "unable to use java 9 Unsafe.invokeCleaner", e);
|
||||||
|
|
||||||
|
// sun.misc.Cleaner unmapping (Java 8 and older)
|
||||||
|
final Method m = DIRECT_BUFFER_CLAZZ.getMethod("cleaner");
|
||||||
|
m.setAccessible(true);
|
||||||
|
final MethodHandle directBufferCleanerMethod = lookup.unreflect(m);
|
||||||
|
final Class<?> cleanerClass = directBufferCleanerMethod.type().returnType();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* "Compile" a MethodHandle that basically is equivalent to the following code:
|
||||||
|
* void unmapper(ByteBuffer byteBuffer)
|
||||||
|
* {
|
||||||
|
* sun.misc.Cleaner cleaner = ((java.nio.DirectByteBuffer) byteBuffer).cleaner();
|
||||||
|
* if (nonNull(cleaner))
|
||||||
|
* {
|
||||||
|
* cleaner.clean();
|
||||||
|
* }
|
||||||
|
* else
|
||||||
|
* {
|
||||||
|
* // the noop is needed because MethodHandles#guardWithTest always needs ELSE
|
||||||
|
* noop(cleaner);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
final MethodHandle cleanMethod = lookup.findVirtual(cleanerClass, "clean", methodType(void.class));
|
||||||
|
final MethodHandle nonNullTest = lookup.findStatic(SnappyFramed.class, "nonNull", methodType(boolean.class, Object.class)).asType(methodType(boolean.class, cleanerClass));
|
||||||
|
final MethodHandle noop = dropArguments(constant(Void.class, null).asType(methodType(void.class)), 0, cleanerClass);
|
||||||
|
handle = filterReturnValue(directBufferCleanerMethod, guardWithTest(nonNullTest, cleanMethod, noop)).asType(methodType(void.class, ByteBuffer.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
cleanHandle = AccessController.doPrivileged(action);
|
||||||
|
|
||||||
|
} catch (Throwable t) {
|
||||||
Logger.getLogger(SnappyFramed.class.getName()).log(Level.FINE, "Exception occurred attempting to lookup Sun specific DirectByteBuffer cleaner classes.", t);
|
Logger.getLogger(SnappyFramed.class.getName()).log(Level.FINE, "Exception occurred attempting to lookup Sun specific DirectByteBuffer cleaner classes.", t);
|
||||||
}
|
}
|
||||||
SUN_BUFFER_CLEANER = bufferCleaner;
|
CLEAN_HANDLE = cleanHandle;
|
||||||
SUN_CLEANER_CLEAN = cleanerClean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -170,18 +231,34 @@ final class SnappyFramed
|
||||||
*
|
*
|
||||||
* @param buffer The {@code ByteBuffer} to release. Must not be {@code null}. Must be {@link ByteBuffer#isDirect() direct}.
|
* @param buffer The {@code ByteBuffer} to release. Must not be {@code null}. Must be {@link ByteBuffer#isDirect() direct}.
|
||||||
*/
|
*/
|
||||||
static void releaseDirectByteBuffer(ByteBuffer buffer)
|
static void releaseDirectByteBuffer(final ByteBuffer buffer)
|
||||||
{
|
{
|
||||||
assert buffer != null && buffer.isDirect();
|
assert buffer != null && buffer.isDirect();
|
||||||
|
|
||||||
if (SUN_DIRECT_BUFFER != null && SUN_DIRECT_BUFFER.isAssignableFrom(buffer.getClass())) {
|
if (CLEAN_HANDLE != null && DIRECT_BUFFER_CLAZZ.isInstance(buffer)) {
|
||||||
try {
|
try {
|
||||||
Object cleaner = SUN_BUFFER_CLEANER.invoke(buffer, (Object[]) null);
|
final PrivilegedExceptionAction<Void> pea = new PrivilegedExceptionAction<Void>() {
|
||||||
SUN_CLEANER_CLEAN.invoke(cleaner, (Object[]) null);
|
@Override
|
||||||
}
|
public Void run() throws Exception {
|
||||||
catch (Throwable t) {
|
try {
|
||||||
|
CLEAN_HANDLE.invokeExact(buffer);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Throwable t) {
|
||||||
|
//this will be an error
|
||||||
|
throw new RuntimeException(t);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
AccessController.doPrivileged(pea);
|
||||||
|
} catch (Throwable t) {
|
||||||
Logger.getLogger(SnappyFramed.class.getName()).log(Level.FINE, "Exception occurred attempting to clean up Sun specific DirectByteBuffer.", t);
|
Logger.getLogger(SnappyFramed.class.getName()).log(Level.FINE, "Exception occurred attempting to clean up Sun specific DirectByteBuffer.", t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static boolean nonNull(Object o) {
|
||||||
|
return o != null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue