--- old/src/java.base/share/native/libjava/RandomAccessFile.c 2015-12-17 13:14:13.881212611 +0530 +++ new/src/java.base/share/native/libjava/RandomAccessFile.c 2015-12-17 13:14:13.725212609 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -103,23 +103,19 @@ JNIEXPORT jlong JNICALL Java_java_io_RandomAccessFile_length(JNIEnv *env, jobject this) { + FD fd; - jlong cur = jlong_zero; - jlong end = jlong_zero; + jlong length = jlong_zero; fd = GET_FD(this, raf_fd); if (fd == -1) { JNU_ThrowIOException(env, "Stream Closed"); return -1; } - if ((cur = IO_Lseek(fd, 0L, SEEK_CUR)) == -1) { - JNU_ThrowIOExceptionWithLastError(env, "Seek failed"); - } else if ((end = IO_Lseek(fd, 0L, SEEK_END)) == -1) { - JNU_ThrowIOExceptionWithLastError(env, "Seek failed"); - } else if (IO_Lseek(fd, cur, SEEK_SET) == -1) { - JNU_ThrowIOExceptionWithLastError(env, "Seek failed"); + if ((length = IO_GetLength(fd)) == -1) { + JNU_ThrowIOExceptionWithLastError(env, "GetLength failed"); } - return end; + return length; } JNIEXPORT void JNICALL --- old/src/java.base/unix/native/libjava/io_util_md.c 2015-12-17 13:14:14.317212618 +0530 +++ new/src/java.base/unix/native/libjava/io_util_md.c 2015-12-17 13:14:14.185212616 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,6 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ - #include "jni.h" #include "jni_util.h" #include "jvm.h" @@ -219,3 +218,14 @@ RESTARTABLE(ftruncate64(fd, length), result); return result; } + +jlong +handleGetLength(FD fd) +{ + struct stat64 sb; + if (fstat64(fd, &sb) == 0) { + return sb.st_size; + } else { + return -1; + } +} --- old/src/java.base/unix/native/libjava/io_util_md.h 2015-12-17 13:14:14.661212623 +0530 +++ new/src/java.base/unix/native/libjava/io_util_md.h 2015-12-17 13:14:14.509212621 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,7 +39,7 @@ ssize_t handleRead(FD fd, void *buf, jint len); jint handleAvailable(FD fd, jlong *pbytes); jint handleSetLength(FD fd, jlong length); - +jlong handleGetLength(FD fd); FD handleOpen(const char *path, int oflag, int mode); /* @@ -72,6 +72,7 @@ #define IO_Append handleWrite #define IO_Available handleAvailable #define IO_SetLength handleSetLength +#define IO_GetLength handleGetLength #ifdef _ALLBSD_SOURCE #define open64 open --- old/src/java.base/windows/native/libjava/io_util_md.c 2015-12-17 13:14:15.161212631 +0530 +++ new/src/java.base/windows/native/libjava/io_util_md.c 2015-12-17 13:14:14.905212627 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -584,3 +584,14 @@ } return long_to_jlong(pos.QuadPart); } + +jlong +handleGetLength(FD fd) { + HANDLE h = (HANDLE) fd; + LARGE_INTEGER length; + if (GetFileSizeEx(h, &length) != 0) { + return long_to_jlong(length.QuadPart); + } else { + return -1; + } +} --- old/src/java.base/windows/native/libjava/io_util_md.h 2015-12-17 13:14:15.509212636 +0530 +++ new/src/java.base/windows/native/libjava/io_util_md.h 2015-12-17 13:14:15.369212634 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,6 +44,7 @@ int handleAvailable(FD fd, jlong *pbytes); int handleSync(FD fd); int handleSetLength(FD fd, jlong length); +jlong handleGetLength(FD fd); JNIEXPORT jint handleRead(FD fd, void *buf, jint len); jint handleWrite(FD fd, const void *buf, jint len); jint handleAppend(FD fd, const void *buf, jint len); @@ -84,6 +85,7 @@ #define IO_Lseek handleLseek #define IO_Available handleAvailable #define IO_SetLength handleSetLength +#define IO_GetLength handleGetLength /* * Setting the handle field in Java_java_io_FileDescriptor_set for --- /dev/null 2015-12-17 12:38:08.853180125 +0530 +++ new/test/java/io/RandomAccessFile/FileLengthTest.java 2015-12-17 13:14:15.733212639 +0530 @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE randomAccessFile that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4823133 + * @summary optimize RandomAccessFile.length() and length() is thread safe now. + */ +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.RandomAccessFile; + +/** + * + * @author vyom.tewari@oracle.com + */ +public class FileLengthTest { + + private static final int BUF_SIZE = 4096; + private static RandomAccessFile randomAccessFile; + private static Thread fileLengthCaller; + private static Thread fileContentReader; + private static StringBuilder fileContents; + private static volatile boolean isFailed = false; + + /** + * this thread will call length() in loop + */ + private static void startLengthThread() { + if (randomAccessFile == null) { + return; + } + fileLengthCaller = new Thread(() -> { + while (true) { + try { + long length = randomAccessFile.length(); + if (length < 0) { + return; + } + } catch (IOException ex) { + return; + } + } + }); + fileLengthCaller.setName("RandomAccessFile-length-caller"); + fileLengthCaller.setDaemon(true); + fileLengthCaller.start(); + } + + /** + * this thread will call read() and store the content in internal buffer. + */ + private static void startReaderThread() { + if (randomAccessFile == null) { + return; + } + fileContentReader = new Thread(() -> { + StringBuilder sb = new StringBuilder(BUF_SIZE); + int i; + byte arr[] = new byte[8]; + try { + while ((i = randomAccessFile.read(arr)) != -1) { + sb.append(new String(arr, 0, i)); + } + if (!sb.toString().equals(fileContents.toString())) { + isFailed = true; + } + } catch (IOException ex) { + } + }); + fileContentReader.setName("RandomAccessFile-content-reader"); + fileContentReader.setDaemon(true); + fileContentReader.start(); + } + + public static void main(String args[]) { + byte arr[] = new byte[BUF_SIZE]; + String testFile = "testfile.txt"; + try { + createDummyFile(testFile); + File file = new File(testFile); + file.deleteOnExit(); + randomAccessFile = new RandomAccessFile(file, "r"); + int count = randomAccessFile.read(arr); + randomAccessFile.seek(0); + fileContents = new StringBuilder(BUF_SIZE); + fileContents.append(new String(arr, 0, count)); + startLengthThread(); + startReaderThread(); + fileContentReader.join(); + } catch (FileNotFoundException | InterruptedException ex) { + } catch (IOException ex) { + } finally { + try { + randomAccessFile.close(); + } catch (IOException ex) { + } + } + if (isFailed) { + throw new RuntimeException("RandomAccessFile.length() changed the underlying file pointer."); + } + } + + private static void createDummyFile(String fileName) throws FileNotFoundException, IOException { + try (FileOutputStream outputStream = new FileOutputStream(new File(fileName))) { + String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + int count = 0; + while ((count + str.length()) < BUF_SIZE) { + outputStream.write(str.getBytes()); + count += str.length(); + } + outputStream.flush(); + } + } +}