netty5/common/src/main/java/io/netty/util/internal/CleanerJava6.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);
}
}
}