Fix and simplify freeing a direct buffer / Fix Android support

Motivation:

6e8ba291cf introduced a regression in Android because Android does not have sun.nio.ch.DirectBuffer (see #2330.)  I also found PlatformDependent0.freeDirectBuffer() and freeDirectBufferUnsafe() are pretty much same after the commit and the unsafe version should be removed.

Modifications:

- Merge PlatformDependent0.freeDirectBuffer() and freeDirectBufferUnsafe() into one method.
- Make the Unsafe unavailable when sun.nio.ch.DirectBuffer is unavailable.  We could keep the Unsafe available and handle the sun.nio.ch.DirectBuffer case separately, but I don't want to complicate our code just because of that.  All supported JDK versions have sun.nio.ch.DirectBuffer if the Unsafe is available.

Result:

Simpler code. Fixes Android support (#2330)
This commit is contained in:
Trustin Lee 2014-03-20 11:11:07 +09:00
parent cf2c8b40ae
commit 2afe58f5f1
2 changed files with 17 additions and 31 deletions

View File

@ -251,12 +251,8 @@ 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 (buffer.isDirect()) { if (hasUnsafe()) {
if (hasUnsafe()) { PlatformDependent0.freeDirectBuffer(buffer);
PlatformDependent0.freeDirectBufferUnsafe(buffer);
} else {
PlatformDependent0.freeDirectBuffer(buffer);
}
} }
} }

View File

@ -48,39 +48,40 @@ final class PlatformDependent0 {
private static final boolean UNALIGNED; private static final boolean UNALIGNED;
static { static {
ByteBuffer direct = ByteBuffer.allocateDirect(1); boolean directBufferFreeable = false;
Field cleanerField;
try { try {
cleanerField = direct.getClass().getDeclaredField("cleaner"); Class<?> cls = Class.forName("sun.nio.ch.DirectBuffer", false, PlatformDependent0.class.getClassLoader());
cleanerField.setAccessible(true); Method method = cls.getMethod("cleaner");
Cleaner cleaner = (Cleaner) cleanerField.get(direct); if ("sun.misc.Cleaner".equals(method.getReturnType().getName())) {
cleaner.clean(); directBufferFreeable = true;
}
} catch (Throwable t) { } catch (Throwable t) {
cleanerField = null; // We don't have sun.nio.ch.DirectBuffer.cleaner().
} }
logger.debug("java.nio.ByteBuffer.cleaner: {}", cleanerField != null? "available" : "unavailable"); logger.debug("sun.nio.ch.DirectBuffer.cleaner(): {}", directBufferFreeable? "available" : "unavailable");
Field addressField; Field addressField;
try { try {
addressField = Buffer.class.getDeclaredField("address"); addressField = Buffer.class.getDeclaredField("address");
addressField.setAccessible(true); addressField.setAccessible(true);
if (addressField.getLong(ByteBuffer.allocate(1)) != 0) { if (addressField.getLong(ByteBuffer.allocate(1)) != 0) {
// A heap buffer must have 0 address.
addressField = null; addressField = null;
} else { } else {
direct = ByteBuffer.allocateDirect(1); ByteBuffer direct = ByteBuffer.allocateDirect(1);
if (addressField.getLong(direct) == 0) { if (addressField.getLong(direct) == 0) {
// A direct buffer must have non-zero address.
addressField = null; addressField = null;
} }
Cleaner cleaner = (Cleaner) cleanerField.get(direct);
cleaner.clean();
} }
} catch (Throwable t) { } catch (Throwable t) {
// Failed to access the address field.
addressField = null; addressField = null;
} }
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 && directBufferFreeable) {
try { try {
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true); unsafeField.setAccessible(true);
@ -104,6 +105,7 @@ final class PlatformDependent0 {
throw e; throw e;
} }
} catch (Throwable cause) { } catch (Throwable cause) {
// Unsafe.copyMemory(Object, long, Object, long, long) unavailable.
unsafe = null; unsafe = null;
} }
} else { } else {
@ -111,6 +113,7 @@ final class PlatformDependent0 {
// Let's just pretend unsafe is unavailable for overall simplicity. // Let's just pretend unsafe is unavailable for overall simplicity.
unsafe = null; unsafe = null;
} }
UNSAFE = unsafe; UNSAFE = unsafe;
if (unsafe == null) { if (unsafe == null) {
@ -144,19 +147,6 @@ final class PlatformDependent0 {
UNSAFE.throwException(t); UNSAFE.throwException(t);
} }
static void freeDirectBufferUnsafe(ByteBuffer buffer) {
try {
Cleaner cleaner = ((DirectBuffer) buffer).cleaner();
if (cleaner == null) {
throw new IllegalArgumentException(
"attempted to deallocate the buffer which was allocated via JNIEnv->NewDirectByteBuffer()");
}
cleaner.clean();
} catch (Throwable t) {
// Nothing we can do here.
}
}
static void freeDirectBuffer(ByteBuffer buffer) { static void freeDirectBuffer(ByteBuffer buffer) {
if (!(buffer instanceof DirectBuffer)) { if (!(buffer instanceof DirectBuffer)) {
return; return;