Skip to content

Commit

Permalink
IGNITE-20515 Fix MappedFile.map for JDK 14 and newer. (#10961)
Browse files Browse the repository at this point in the history
  • Loading branch information
ivandasch authored Oct 2, 2023
1 parent d93a9f3 commit eeb5535
Showing 1 changed file with 163 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
}

/**
Expand All @@ -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());
}
}
}
}

0 comments on commit eeb5535

Please sign in to comment.