150 lines
5.5 KiB
Java
150 lines
5.5 KiB
Java
/*
|
|
* Copyright 2014 The Netty Project
|
|
*
|
|
* The Netty Project licenses this file to you under the Apache License,
|
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
package io.netty.util.internal;
|
|
|
|
import io.netty.util.internal.logging.InternalLogger;
|
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
|
|
|
import java.lang.invoke.MethodHandle;
|
|
import java.lang.invoke.MethodHandles;
|
|
import java.lang.invoke.MethodType;
|
|
import java.lang.reflect.Field;
|
|
import java.nio.ByteBuffer;
|
|
import java.security.AccessController;
|
|
import java.security.PrivilegedAction;
|
|
|
|
|
|
/**
|
|
* Allows to free direct {@link ByteBuffer} by using Cleaner. This is encapsulated in an extra class to be able
|
|
* to use {@link PlatformDependent0} on Android without problems.
|
|
*
|
|
* For more details see <a href="https://github.com/netty/netty/issues/2604">#2604</a>.
|
|
*/
|
|
final class CleanerJava6 implements Cleaner {
|
|
private static final long CLEANER_FIELD_OFFSET;
|
|
private static final MethodHandle CLEAN_HANDLE;
|
|
private static final MethodHandle CLEANER_FIELD_HANDLE;
|
|
|
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(CleanerJava6.class);
|
|
|
|
static {
|
|
long fieldOffset;
|
|
MethodHandle cleanHandle = null;
|
|
MethodHandle cleanerFieldHandle = null;
|
|
Throwable error = null;
|
|
final ByteBuffer direct = ByteBuffer.allocateDirect(1);
|
|
try {
|
|
Object mayBeCleanerField = AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
|
|
try {
|
|
Field cleanerField1 = direct.getClass().getDeclaredField("cleaner");
|
|
if (!PlatformDependent.hasUnsafe()) {
|
|
// We need to make it accessible if we do not use Unsafe as we will access it via
|
|
// reflection.
|
|
cleanerField1.setAccessible(true);
|
|
}
|
|
return cleanerField1;
|
|
} catch (Throwable cause) {
|
|
return cause;
|
|
}
|
|
});
|
|
if (mayBeCleanerField instanceof Throwable) {
|
|
throw (Throwable) mayBeCleanerField;
|
|
}
|
|
|
|
Field cleanerField = (Field) mayBeCleanerField;
|
|
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
|
|
|
final Object cleaner;
|
|
|
|
// If we have sun.misc.Unsafe we will use it as its faster then using reflection,
|
|
// otherwise let us try reflection as last resort.
|
|
if (PlatformDependent.hasUnsafe()) {
|
|
fieldOffset = PlatformDependent0.objectFieldOffset(cleanerField);
|
|
cleaner = PlatformDependent0.getObject(direct, fieldOffset);
|
|
} else {
|
|
fieldOffset = -1;
|
|
cleaner = cleanerField.get(direct);
|
|
cleanerFieldHandle = lookup.unreflectGetter(cleanerField);
|
|
}
|
|
cleanHandle = lookup.findVirtual(cleaner.getClass(), "clean", MethodType.methodType(void.class));
|
|
cleanHandle.invoke(cleaner);
|
|
} catch (Throwable t) {
|
|
// We don't have ByteBuffer.cleaner().
|
|
fieldOffset = -1;
|
|
cleanerFieldHandle = null;
|
|
cleanHandle = null;
|
|
error = t;
|
|
}
|
|
|
|
if (error == null) {
|
|
logger.debug("java.nio.ByteBuffer.cleaner(): available");
|
|
} else {
|
|
logger.debug("java.nio.ByteBuffer.cleaner(): unavailable", error);
|
|
}
|
|
CLEANER_FIELD_HANDLE = cleanerFieldHandle;
|
|
CLEANER_FIELD_OFFSET = fieldOffset;
|
|
CLEAN_HANDLE = cleanHandle;
|
|
}
|
|
|
|
static boolean isSupported() {
|
|
return CLEANER_FIELD_OFFSET != -1 || CLEANER_FIELD_HANDLE != null;
|
|
}
|
|
|
|
@Override
|
|
public void freeDirectBuffer(ByteBuffer buffer) {
|
|
if (!buffer.isDirect()) {
|
|
return;
|
|
}
|
|
if (System.getSecurityManager() == null) {
|
|
try {
|
|
freeDirectBuffer0(buffer);
|
|
} catch (Throwable cause) {
|
|
PlatformDependent0.throwException(cause);
|
|
}
|
|
} else {
|
|
freeDirectBufferPrivileged(buffer);
|
|
}
|
|
}
|
|
|
|
private static void freeDirectBufferPrivileged(final ByteBuffer buffer) {
|
|
Throwable cause = AccessController.doPrivileged((PrivilegedAction<Throwable>) () -> {
|
|
try {
|
|
freeDirectBuffer0(buffer);
|
|
return null;
|
|
} catch (Throwable cause1) {
|
|
return cause1;
|
|
}
|
|
});
|
|
if (cause != null) {
|
|
PlatformDependent0.throwException(cause);
|
|
}
|
|
}
|
|
|
|
private static void freeDirectBuffer0(ByteBuffer buffer) throws Throwable {
|
|
final Object cleaner;
|
|
// If CLEANER_FIELD_OFFSET == -1 we need to use reflection to access the cleaner, otherwise we can use
|
|
// sun.misc.Unsafe.
|
|
if (CLEANER_FIELD_OFFSET == -1) {
|
|
cleaner = CLEANER_FIELD_HANDLE.invoke(buffer);
|
|
} else {
|
|
cleaner = PlatformDependent0.getObject(buffer, CLEANER_FIELD_OFFSET);
|
|
}
|
|
if (cleaner != null) {
|
|
CLEAN_HANDLE.invoke(cleaner);
|
|
}
|
|
}
|
|
}
|