Skip to content

Commit

Permalink
fix: add more probing methods for map0 implementation (#81)
Browse files Browse the repository at this point in the history
* fix: add more probing methods for `map0` implementation

* fix: correctly name `map` method

* fix: support newer `unmap0` implementations

* fix(GH-81): don't reference `f.getChannel()` when not needed

* fix(GH-81): specify `null` as receiver of `static` methods
  • Loading branch information
JarvisCraft authored May 14, 2024
1 parent f9a931e commit 89e5529
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 18 deletions.
128 changes: 110 additions & 18 deletions src/one/nio/mem/MappedFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package one.nio.mem;

import java.lang.reflect.Constructor;
import one.nio.os.Mem;
import one.nio.serial.DataStream;
import one.nio.util.JavaInternals;
Expand Down Expand Up @@ -160,22 +161,72 @@ public static long map(RandomAccessFile f, int mode, long start, long size) thro
}

try {
Class<?> cls = Class.forName("sun.nio.ch.FileChannelImpl");
Method map0 = JavaInternals.getMethod(cls, "map0", int.class, long.class, long.class);
if (map0 != null) {
return (long) map0.invoke(f.getChannel(), mode, start, size);
Class<?> fileChannelImplClass = JavaInternals.getClass("sun.nio.ch.FileChannelImpl");
if (fileChannelImplClass != null) {
Method map0 = JavaInternals.getMethod(
fileChannelImplClass, "map0", int.class, long.class, long.class
);
if (map0 != null) {
return (long) map0.invoke(f.getChannel(), mode, start, size);
}
// Newer JDK has an extra 'sync' argument
map0 = JavaInternals.getMethod(
fileChannelImplClass, "map0", int.class, long.class, long.class, boolean.class
);
if (map0 != null) {
return (long) map0.invoke(f.getChannel(), mode, start, size, false);
}
// Since 19 JDK has an extra 'file descriptor' argument
map0 = JavaInternals.getMethod(
fileChannelImplClass, "map0", FileDescriptor.class, int.class, long.class, long.class, boolean.class
);
if (map0 != null) {
return (long) map0.invoke(f.getChannel(), f.getFD(), mode, start, size, false);
}
}
// Newer JDK has an extra 'sync' argument
map0 = JavaInternals.getMethod(cls, "map0", int.class, long.class, long.class, boolean.class);
if (map0 != null) {
return (long) map0.invoke(f.getChannel(), mode, start, size, false);
// JDKs since 20 have the `map0` method moved into the new `FileDispatcherImpl` class as `map`
// In reality, the `static native long map0` method is still available via `UnixFileDispatcherImpl`
// so it is also probed as an alternative (we don't know which one will get removed earlier /shrug)
// See https://github.com/openjdk/jdk/commit/48cc15602b62e81bb179ca9570a1e7d8bbf4d6df
Class<?> fileDispatcherImplClass = JavaInternals.getClass("sun.nio.ch.FileDispatcherImpl");
if (fileDispatcherImplClass != null) {
Method map0 = JavaInternals.getMethodRecursively(fileDispatcherImplClass,
"map", FileDescriptor.class, int.class, long.class, long.class, boolean.class
);
if (map0 != null) {
Constructor<?> fileDispatcherImplConstructor = JavaInternals.getConstructor(fileDispatcherImplClass);
if (fileDispatcherImplConstructor != null) {
Object fileDispatcherImpl = fileDispatcherImplConstructor.newInstance();
return (long) map0.invoke(
fileDispatcherImpl,
f.getFD(), mode, start, size, false
);
}
}
}
// Since 19 JDK has an extra 'file descriptor' argument
map0 = JavaInternals.getMethod(
cls, "map0", FileDescriptor.class, int.class, long.class, long.class, boolean.class);
if (map0 != null) {
return (long) map0.invoke(f.getFD(), f.getChannel(), mode, start, size, false);
Class<?> unixFileDispatcherImplClass = JavaInternals.getClass("sun.nio.ch.UnixFileDispatcherImpl");
if (unixFileDispatcherImplClass != null) {
Method map0 = JavaInternals.getMethod(
unixFileDispatcherImplClass, "map0", FileDescriptor.class, int.class, long.class, long.class, boolean.class
);
if (map0 != null) {
return (long) map0.invoke(null, f.getFD(), mode, start, size, false);
}
// we also probe the non-static `map` method on this class...
map0 = JavaInternals.getMethodRecursively(
unixFileDispatcherImplClass, "map", FileDescriptor.class, int.class, long.class, long.class, boolean.class
);
if (map0 != null) {
Constructor<?> unixFileDispatcherImplConstructor = JavaInternals.getConstructor(unixFileDispatcherImplClass);
if (unixFileDispatcherImplConstructor != null) {
Object unixFileDispatcherImplObject = unixFileDispatcherImplConstructor.newInstance();
return (long) map0.invoke(unixFileDispatcherImplObject,
f.getFD(), mode, start, size, false
);
}
}
}

throw new IllegalStateException("map0 method not found");
} catch (InvocationTargetException e) {
Throwable target = e.getTargetException();
Expand All @@ -193,12 +244,53 @@ public static void unmap(long start, long size) {
}

try {
Class<?> cls = Class.forName("sun.nio.ch.FileChannelImpl");
Method unmap0 = JavaInternals.getMethod(cls, "unmap0", long.class, long.class);
if (unmap0 == null) {
throw new IllegalStateException("unmap0 method not found");
Class<?> fileChannelImplClass = JavaInternals.getClass("sun.nio.ch.FileChannelImpl");
if (fileChannelImplClass != null) {
Method unmap0 = JavaInternals.getMethod(fileChannelImplClass, "unmap0", long.class, long.class);
if (unmap0 != null) {
unmap0.invoke(null, start, size);
return;
}
}

// JDKs since 20 have the `unmap0` method moved into the new `FileDispatcherImpl` class as `unmap`
// In reality, the `static native long unmap0` method is still available via `UnixFileDispatcherImpl`
// so it is also probed as an alternative (we don't know which one will get removed earlier /shrug)
// See https://github.com/openjdk/jdk/commit/48cc15602b62e81bb179ca9570a1e7d8bbf4d6df
Class<?> fileDispatcherImplClass = JavaInternals.getClass("sun.nio.ch.FileDispatcherImpl");
if (fileDispatcherImplClass != null) {
Method unmap0 = JavaInternals.getMethodRecursively(fileDispatcherImplClass, "unmap", long.class, long.class);
if (unmap0 != null) {
Constructor<?> fileDispatcherImplConstructor = JavaInternals.getConstructor(fileDispatcherImplClass);
if (fileDispatcherImplConstructor != null) {
Object fileDispatcherImpl = fileDispatcherImplConstructor.newInstance();
unmap0.invoke(fileDispatcherImpl, start, size);
return;
}
}
}
unmap0.invoke(null, start, size);
Class<?> unixFileDispatcherImplClass = JavaInternals.getClass("sun.nio.ch.UnixFileDispatcherImpl");
if (unixFileDispatcherImplClass != null) {
Method unmap0 = JavaInternals.getMethod(
unixFileDispatcherImplClass, "map0", FileDescriptor.class, int.class, long.class, long.class, boolean.class
);
if (unmap0 != null) {
unmap0.invoke(null, start, size);
return;
}
// we also probe the non-static `map` method on this class...
unmap0 = JavaInternals.getMethodRecursively(unixFileDispatcherImplClass, "unmap", long.class, long.class);
if (unmap0 != null) {
Constructor<?> unixFileDispatcherImplConstructor = JavaInternals.getConstructor(unixFileDispatcherImplClass);
if (unixFileDispatcherImplConstructor != null) {
Object unixFileDispatcherImplObject = unixFileDispatcherImplConstructor.newInstance();
unmap0.invoke(unixFileDispatcherImplObject, start, size);
return;
}
}
}

throw new IllegalStateException("unmap0 method not found");
} catch (ReflectiveOperationException e) {
throw new IllegalStateException(e);
}
Expand Down
14 changes: 14 additions & 0 deletions src/one/nio/util/JavaInternals.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ private static long getAccessibleOffset() {
return 0;
}

public static Class<?> getClass(String className) {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
return null;
}
}

public static Field getField(Class<?> cls, String name) {
Field f = findField(cls, name);
if (f != null) setAccessible(f);
Expand Down Expand Up @@ -101,6 +109,12 @@ public static Method getMethod(Class<?> cls, String name, Class<?>... params) {
return m;
}

public static Method getMethodRecursively(Class<?> cls, String name, Class<?>... params) {
Method m = findMethodRecursively(cls, name, params);
if (m != null) setAccessible(m);
return m;
}

public static Method findMethod(Class<?> cls, String name, Class<?>... params) {
try {
return cls.getDeclaredMethod(name, params);
Expand Down

0 comments on commit 89e5529

Please sign in to comment.