diff --git a/test/jdk/java/nio/channels/FileChannel/directio/DirectIOTest.java b/test/jdk/java/nio/channels/FileChannel/directio/DirectIOTest.java index 34f2bacc7dd..b9dd309d0f7 100644 --- a/test/jdk/java/nio/channels/FileChannel/directio/DirectIOTest.java +++ b/test/jdk/java/nio/channels/FileChannel/directio/DirectIOTest.java @@ -27,11 +27,13 @@ * @summary Test for ExtendedOpenOption.DIRECT flag * @requires (os.family == "linux" | os.family == "aix") * @library /test/lib + * @modules java.base/sun.nio.ch:+open java.base/java.io:+open * @build jdk.test.lib.Platform * @run main/native DirectIOTest */ import java.io.*; +import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.channels.*; @@ -47,10 +49,25 @@ public class DirectIOTest { private static final int BASE_SIZE = 4096; + private static final int TRIES = 3; + + public static int getFD(FileChannel channel) throws Exception { + Field fFdFd = channel.getClass().getDeclaredField("fd"); + fFdFd.setAccessible(true); + FileDescriptor fd = (FileDescriptor) fFdFd.get(channel); + + Field fFd = FileDescriptor.class.getDeclaredField("fd"); + fFd.setAccessible(true); + return fFd.getInt(fd); + } + + private static void testWrite(Path p, long blockSize) throws Exception { + try (FileChannel fc = FileChannel.open(p, + StandardOpenOption.READ, + StandardOpenOption.WRITE, + ExtendedOpenOption.DIRECT)) { + int fd = getFD(fc); - private static int testWrite(Path p, long blockSize) throws Exception { - try (FileChannel fc = FileChannel.open(p, StandardOpenOption.WRITE, - ExtendedOpenOption.DIRECT)) { int bs = (int)blockSize; int size = Math.max(BASE_SIZE, bs); int alignment = bs; @@ -60,22 +77,55 @@ private static int testWrite(Path p, long blockSize) throws Exception { for (int j = 0; j < size; j++) { src.put((byte)0); } - src.flip(); - fc.write(src); - return size; + + // If there is AV or other FS tracing software, it may cache the file + // contents on first access, even though we have asked for DIRECT here. + // Do several attempts to make test more resilient. + + for (int t = 0; t < TRIES; t++) { + flushFileCache(size, fd); + src.flip(); + fc.position(0); + fc.write(src); + if (!isFileInCache(size, fd)) { + return; + } + } + + throw new RuntimeException("DirectIO is not working properly with " + + "write. File still exists in cache!"); } } - private static int testRead(Path p, long blockSize) throws Exception { - try (FileChannel fc = FileChannel.open(p, ExtendedOpenOption.DIRECT)) { + private static void testRead(Path p, long blockSize) throws Exception { + try (FileChannel fc = FileChannel.open(p, + StandardOpenOption.READ, + ExtendedOpenOption.DIRECT)) { + int fd = getFD(fc); + int bs = (int)blockSize; int size = Math.max(BASE_SIZE, bs); int alignment = bs; ByteBuffer dest = ByteBuffer.allocateDirect(size + alignment - 1) .alignedSlice(alignment); assert dest.capacity() != 0; - fc.read(dest); - return size; + + // If there is AV or other FS tracing software, it may cache the file + // contents on first access, even though we have asked for DIRECT here. + // Do several attempts to make test more resilient. + + for (int t = 0; t < TRIES; t++) { + flushFileCache(size, fd); + dest.clear(); + fc.position(0); + fc.read(dest); + if (!isFileInCache(size, fd)) { + return; + } + } + + throw new RuntimeException("DirectIO is not working properly with " + + "read. File still exists in cache!"); } } @@ -84,12 +134,8 @@ public static Path createTempFile() throws IOException { Paths.get(System.getProperty("test.dir", ".")), "test", null); } - private static boolean isFileInCache(int size, Path p) { - String path = p.toString(); - return isFileInCache0(size, path); - } - - private static native boolean isFileInCache0(int size, String path); + private static native boolean flushFileCache(int size, int fd); + private static native boolean isFileInCache(int size, int fd); public static void main(String[] args) throws Exception { Path p = createTempFile(); @@ -98,16 +144,8 @@ public static void main(String[] args) throws Exception { System.loadLibrary("DirectIO"); try { - int size = testWrite(p, blockSize); - if (isFileInCache(size, p)) { - throw new RuntimeException("DirectIO is not working properly with " - + "write. File still exists in cache!"); - } - size = testRead(p, blockSize); - if (isFileInCache(size, p)) { - throw new RuntimeException("DirectIO is not working properly with " - + "read. File still exists in cache!"); - } + testWrite(p, blockSize); + testRead(p, blockSize); } finally { Files.delete(p); } diff --git a/test/jdk/java/nio/channels/FileChannel/directio/libDirectIO.c b/test/jdk/java/nio/channels/FileChannel/directio/libDirectIO.c index 4897500bf2d..5eea51da4c7 100644 --- a/test/jdk/java/nio/channels/FileChannel/directio/libDirectIO.c +++ b/test/jdk/java/nio/channels/FileChannel/directio/libDirectIO.c @@ -45,13 +45,27 @@ static void ThrowException(JNIEnv *env, const char *name, const char *msg) { /* * Class: DirectIO - * Method: isFileInCache0 - * Signature: (ILjava/lang/String;)Z + * Method: flushFileCache + * Signature: (II;)V */ -JNIEXPORT jboolean Java_DirectIOTest_isFileInCache0(JNIEnv *env, +JNIEXPORT void Java_DirectIOTest_flushFileCache(JNIEnv *env, jclass cls, jint file_size, - jstring file_path) { + jint fd) { +#ifdef __linux__ + posix_fadvise(fd, 0, file_size, POSIX_FADV_DONTNEED); +#endif +} + +/* + * Class: DirectIO + * Method: isFileInCache + * Signature: (II;)Z + */ +JNIEXPORT jboolean Java_DirectIOTest_isFileInCache(JNIEnv *env, + jclass cls, + jint file_size, + jint fd) { void *f_mmap; #ifdef __linux__ unsigned char *f_seg; @@ -69,17 +83,10 @@ JNIEXPORT jboolean Java_DirectIOTest_isFileInCache0(JNIEnv *env, size_t index = (file_size + page_size - 1) /page_size; jboolean result = JNI_FALSE; - const char* path = (*env)->GetStringUTFChars(env, file_path, JNI_FALSE); - - int fd = open(path, O_RDWR); - - (*env)->ReleaseStringUTFChars(env, file_path, path); - f_mmap = mmap(0, file_size, PROT_NONE, MAP_SHARED, fd, 0); if (f_mmap == MAP_FAILED) { - close(fd); ThrowException(env, "java/io/IOException", - "test of whether file exists in cache failed"); + "test of whether file exists in cache failed: mmap failed"); } f_seg = malloc(index); if (f_seg != NULL) { @@ -95,9 +102,8 @@ JNIEXPORT jboolean Java_DirectIOTest_isFileInCache0(JNIEnv *env, free(f_seg); } else { ThrowException(env, "java/io/IOException", - "test of whether file exists in cache failed"); + "test of whether file exists in cache failed: malloc failed"); } - close(fd); munmap(f_mmap, file_size); return result; }