diff --git a/modules/core/src/main/java/org/apache/ignite/internal/mem/file/MappedFile.java b/modules/core/src/main/java/org/apache/ignite/internal/mem/file/MappedFile.java index 4b7552491c61f..f264d87a31516 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/mem/file/MappedFile.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/mem/file/MappedFile.java @@ -19,26 +19,28 @@ import java.io.Closeable; import java.io.File; +import java.io.FileDescriptor; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.mem.DirectMemoryRegion; import org.apache.ignite.internal.mem.UnsafeChunk; import org.apache.ignite.internal.util.typedef.internal.U; import sun.nio.ch.FileChannelImpl; +import static org.apache.ignite.internal.util.IgniteUtils.jdkVersion; +import static org.apache.ignite.internal.util.IgniteUtils.majorJavaVersion; + /** */ public class MappedFile implements Closeable, DirectMemoryRegion { - /** */ - private static final Method map0 = U.findNonPublicMethod(FileChannelImpl.class, "map0", int.class, long.class, long.class); - - /** */ - private static final Method unmap0 = U.findNonPublicMethod(FileChannelImpl.class, "unmap0", long.class, long.class); - /** */ public static final int MAP_RW = 1; + /** File memory mapper */ + private static final Mapper mapper = pickMapper(); + /** */ private final RandomAccessFile file; @@ -85,7 +87,7 @@ public final RandomAccessFile file() { /** {@inheritDoc} */ @Override public void close() throws IOException { try { - unmap(addr, size); + mapper.unmap(addr, size); } finally { file.close(); @@ -122,16 +124,7 @@ public final RandomAccessFile file() { * @throws IOException */ public static long map(RandomAccessFile f, int mode, long start, long size) throws IOException { - try { - return (Long)map0.invoke(f.getChannel(), mode, start, size); - } - catch (IllegalAccessException e) { - throw new IllegalStateException(e); - } - catch (InvocationTargetException e) { - Throwable target = e.getTargetException(); - throw (target instanceof IOException) ? (IOException)target : new IOException(target); - } + return mapper.map(f, mode, start, size); } /** @@ -141,14 +134,161 @@ public static long map(RandomAccessFile f, int mode, long start, long size) thro * @param size Size of the mapped file. */ public static void unmap(long addr, long size) { - try { - unmap0.invoke(null, addr, size); + mapper.unmap(addr, size); + } + + /** */ + private static Mapper pickMapper() { + int javaVersion = majorJavaVersion(jdkVersion()); + + if (javaVersion >= 19) + return new JDK19Mapper(); + + if (javaVersion >= 14) + return new JDK14Mapper(); + + return new LegacyMapper(); + } + + /** */ + private interface Mapper { + /** */ + long map(RandomAccessFile f, int mode, long start, long size) throws IOException; + + /** */ + void unmap(long addr, long size); + } + + /** */ + private static class LegacyMapper implements Mapper { + /** */ + private static final Method map0 = U.findNonPublicMethod(FileChannelImpl.class, "map0", int.class, long.class, long.class); + + /** */ + private static final Method unmap0 = U.findNonPublicMethod(FileChannelImpl.class, "unmap0", long.class, long.class); + + + /** {@inheritDoc} */ + @Override public long map(RandomAccessFile f, int mode, long start, long size) throws IOException { + try { + return (Long)map0.invoke(f.getChannel(), mode, start, size); + } + catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + catch (InvocationTargetException e) { + Throwable target = e.getTargetException(); + throw (target instanceof IOException) ? (IOException)target : new IOException(target); + } } - catch (IllegalAccessException e) { - throw new IllegalStateException(e); + + /** {@inheritDoc} */ + @Override public void unmap(long addr, long size) { + try { + unmap0.invoke(null, addr, size); + } + catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + catch (InvocationTargetException e) { + throw new IllegalStateException(e.getTargetException()); + } } - catch (InvocationTargetException e) { - throw new IllegalStateException(e.getTargetException()); + } + + /** */ + private static class JDK14Mapper extends LegacyMapper { + /** Method {@link FileChannelImpl#map0} has additional parameter since JDK 14, isSync, {@code false} by default. */ + private static final Method map0 = U.findNonPublicMethod(FileChannelImpl.class, "map0", int.class, long.class, + long.class, boolean.class); + + /** {@inheritDoc} */ + @Override public long map(RandomAccessFile f, int mode, long start, long size) throws IOException { + try { + return (Long)map0.invoke(f.getChannel(), mode, start, size, false); + } + catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + catch (InvocationTargetException e) { + Throwable target = e.getTargetException(); + throw (target instanceof IOException) ? (IOException)target : new IOException(target); + } + } + } + + /** */ + private static class JDK19Mapper implements Mapper { + /** */ + private static final Method map; + + /** */ + private static final Method unmap; + + /** */ + private static final Object dispatcher; + + static { + try { + // These methods are still in {@link FileChannelImpl} class in JDK 19. + Method map0 = U.findNonPublicMethod(FileChannelImpl.class, "map0", FileDescriptor.class, int.class, + long.class, long.class, boolean.class); + + Method unmap0 = U.findNonPublicMethod(FileChannelImpl.class, "unmap0", long.class, long.class); + + if (map0 != null && unmap0 != null) { + map = map0; + unmap = unmap0; + dispatcher = null; + } + else { + // That methods are moved to {@link sun.nio.ch.FileDispatcher}. + Class fileDispatcherCls = Class.forName("sun.nio.ch.FileDispatcher"); + + dispatcher = U.staticField(FileChannelImpl.class, "nd"); + + map = U.findNonPublicMethod(fileDispatcherCls, "map", FileDescriptor.class, int.class, + long.class, long.class, boolean.class); + + unmap = U.findNonPublicMethod(fileDispatcherCls, "unmap", long.class, long.class); + + } + } + catch (ClassNotFoundException | IgniteCheckedException e) { + throw new ExceptionInInitializerError(e); + } + } + + /** {@inheritDoc} */ + @Override public long map(RandomAccessFile f, int mode, long start, long size) throws IOException { + try { + Object fd = U.field(f.getChannel(), "fd"); + + if (dispatcher != null) + return (Long)map.invoke(dispatcher, fd, mode, start, size, false); + else + return (Long)map.invoke(f.getChannel(), fd, mode, start, size, false); + } + catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + catch (InvocationTargetException e) { + Throwable target = e.getTargetException(); + throw (target instanceof IOException) ? (IOException)target : new IOException(target); + } + } + + /** {@inheritDoc} */ + @Override public void unmap(long addr, long size) { + try { + unmap.invoke(dispatcher, addr, size); // If dispatcher is null, the static method will be called. + } + catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + catch (InvocationTargetException e) { + throw new IllegalStateException(e.getTargetException()); + } } } }