[#2604] Not try to use sun.misc.Cleaner when on android

Motivation:

When a user tries to use netty on android it currently fails with "Could not find class 'sun.misc.Cleaner'"

Modification:

Encapsulate sun.misc.Cleaner usage in extra class to workaround this isssue.

Result:
Netty can be used on android again
This commit is contained in:
Norman Maurer 2014-06-26 12:48:51 +02:00
parent d062500ca0
commit 522b3e1b92
3 changed files with 81 additions and 33 deletions

View File

@ -0,0 +1,74 @@
/*
* 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 sun.misc.Cleaner;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
/**
* Allows to free direct {@link ByteBuffer} by using {@link 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 Cleaner0 {
private static final long CLEANER_FIELD_OFFSET;
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Cleaner0.class);
static {
ByteBuffer direct = ByteBuffer.allocateDirect(1);
Field cleanerField;
long fieldOffset = -1;
if (PlatformDependent0.hasUnsafe()) {
try {
cleanerField = direct.getClass().getDeclaredField("cleaner");
cleanerField.setAccessible(true);
Cleaner cleaner = (Cleaner) cleanerField.get(direct);
cleaner.clean();
fieldOffset = PlatformDependent0.objectFieldOffset(cleanerField);
} catch (Throwable t) {
// We don't have ByteBuffer.cleaner().
fieldOffset = -1;
}
}
logger.debug("java.nio.ByteBuffer.cleaner(): {}", fieldOffset != -1? "available" : "unavailable");
CLEANER_FIELD_OFFSET = fieldOffset;
// free buffer if possible
freeDirectBuffer(direct);
}
static void freeDirectBuffer(ByteBuffer buffer) {
if (CLEANER_FIELD_OFFSET == -1 || !buffer.isDirect()) {
return;
}
try {
Cleaner cleaner = (Cleaner) PlatformDependent0.getObject(buffer, CLEANER_FIELD_OFFSET);
if (cleaner != null) {
cleaner.clean();
}
} catch (Throwable t) {
// Nothing we can do here.
}
}
private Cleaner0() { }
}

View File

@ -250,7 +250,9 @@ public final class PlatformDependent {
* the current platform does not support this operation or the specified buffer is not a direct buffer. * the current platform does not support this operation or the specified buffer is not a direct buffer.
*/ */
public static void freeDirectBuffer(ByteBuffer buffer) { public static void freeDirectBuffer(ByteBuffer buffer) {
if (hasUnsafe()) { if (hasUnsafe() && !isAndroid()) {
// only direct to method if we are not running on android.
// See https://github.com/netty/netty/issues/2604
PlatformDependent0.freeDirectBuffer(buffer); PlatformDependent0.freeDirectBuffer(buffer);
} }
} }

View File

@ -17,7 +17,6 @@ package io.netty.util.internal;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
import sun.misc.Cleaner;
import sun.misc.Unsafe; import sun.misc.Unsafe;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@ -40,7 +39,6 @@ final class PlatformDependent0 {
private static final Unsafe UNSAFE; private static final Unsafe UNSAFE;
private static final boolean BIG_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN; private static final boolean BIG_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;
private static final long ADDRESS_FIELD_OFFSET; private static final long ADDRESS_FIELD_OFFSET;
private static final long CLEANER_FIELD_OFFSET;
/** /**
* Limits the number of bytes to copy per {@link Unsafe#copyMemory(long, long, long)} to allow safepoint polling * Limits the number of bytes to copy per {@link Unsafe#copyMemory(long, long, long)} to allow safepoint polling
@ -57,19 +55,6 @@ final class PlatformDependent0 {
static { static {
ByteBuffer direct = ByteBuffer.allocateDirect(1); ByteBuffer direct = ByteBuffer.allocateDirect(1);
Field cleanerField;
try {
cleanerField = direct.getClass().getDeclaredField("cleaner");
cleanerField.setAccessible(true);
Cleaner cleaner = (Cleaner) cleanerField.get(direct);
cleaner.clean();
} catch (Throwable t) {
// We don't have ByteBuffer.cleaner().
cleanerField = null;
}
logger.debug("java.nio.ByteBuffer.cleaner(): {}", cleanerField != null? "available" : "unavailable");
Field addressField; Field addressField;
try { try {
addressField = Buffer.class.getDeclaredField("address"); addressField = Buffer.class.getDeclaredField("address");
@ -90,7 +75,7 @@ final class PlatformDependent0 {
logger.debug("java.nio.Buffer.address: {}", addressField != null? "available" : "unavailable"); logger.debug("java.nio.Buffer.address: {}", addressField != null? "available" : "unavailable");
Unsafe unsafe; Unsafe unsafe;
if (addressField != null && cleanerField != null) { if (addressField != null) {
try { try {
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true); unsafeField.setAccessible(true);
@ -127,11 +112,9 @@ final class PlatformDependent0 {
if (unsafe == null) { if (unsafe == null) {
ADDRESS_FIELD_OFFSET = -1; ADDRESS_FIELD_OFFSET = -1;
CLEANER_FIELD_OFFSET = -1;
UNALIGNED = false; UNALIGNED = false;
} else { } else {
ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField); ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField);
CLEANER_FIELD_OFFSET = objectFieldOffset(cleanerField);
boolean unaligned; boolean unaligned;
try { try {
Class<?> bitsClass = Class.forName("java.nio.Bits", false, ClassLoader.getSystemClassLoader()); Class<?> bitsClass = Class.forName("java.nio.Bits", false, ClassLoader.getSystemClassLoader());
@ -148,9 +131,6 @@ final class PlatformDependent0 {
UNALIGNED = unaligned; UNALIGNED = unaligned;
logger.debug("java.nio.Bits.unaligned: {}", UNALIGNED); logger.debug("java.nio.Bits.unaligned: {}", UNALIGNED);
} }
// free temporary buffer if possible
freeDirectBuffer(direct);
} }
static boolean hasUnsafe() { static boolean hasUnsafe() {
@ -162,17 +142,9 @@ final class PlatformDependent0 {
} }
static void freeDirectBuffer(ByteBuffer buffer) { static void freeDirectBuffer(ByteBuffer buffer) {
if (CLEANER_FIELD_OFFSET == -1 || !buffer.isDirect()) { // Delegate to other class to not break on android
return; // See https://github.com/netty/netty/issues/2604
} Cleaner0.freeDirectBuffer(buffer);
try {
Cleaner cleaner = (Cleaner) getObject(buffer, CLEANER_FIELD_OFFSET);
if (cleaner != null) {
cleaner.clean();
}
} catch (Throwable t) {
// Nothing we can do here.
}
} }
static long directBufferAddress(ByteBuffer buffer) { static long directBufferAddress(ByteBuffer buffer) {