--- old/src/share/classes/java/io/FileInputStream.java Thu Sep 8 17:21:20 2011 +++ new/src/share/classes/java/io/FileInputStream.java Thu Sep 8 17:21:19 2011 @@ -56,16 +56,6 @@ private final Object closeLock = new Object(); private volatile boolean closed = false; - private static final ThreadLocal runningFinalize = - new ThreadLocal<>(); - - private static boolean isRunningFinalize() { - Boolean val; - if ((val = runningFinalize.get()) != null) - return val.booleanValue(); - return false; - } - /** * Creates a FileInputStream by * opening a connection to an actual file, @@ -319,10 +309,10 @@ int useCount = fd.decrementAndGetUseCount(); /* - * If FileDescriptor is still in use by another stream, the finalizer + * If FileDescriptor is still in use by another stream, we * will not close it. */ - if ((useCount <= 0) || !isRunningFinalize()) { + if (useCount <= 0) { close0(); } } @@ -391,18 +381,7 @@ */ protected void finalize() throws IOException { if ((fd != null) && (fd != FileDescriptor.in)) { - - /* - * Finalizer should not release the FileDescriptor if another - * stream is still using it. If the user directly invokes - * close() then the FileDescriptor is also released. - */ - runningFinalize.set(Boolean.TRUE); - try { close(); - } finally { - runningFinalize.set(Boolean.FALSE); - } } } } --- old/src/share/classes/java/io/FileOutputStream.java Thu Sep 8 17:21:22 2011 +++ new/src/share/classes/java/io/FileOutputStream.java Thu Sep 8 17:21:22 2011 @@ -63,22 +63,13 @@ private final boolean append; /** - * The associated channel, initalized lazily. + * The associated channel, initialized lazily. */ private FileChannel channel; private final Object closeLock = new Object(); private volatile boolean closed = false; - private static final ThreadLocal runningFinalize = - new ThreadLocal<>(); - private static boolean isRunningFinalize() { - Boolean val; - if ((val = runningFinalize.get()) != null) - return val.booleanValue(); - return false; - } - /** * Creates a file output stream to write to the file with the * specified name. A new FileDescriptor object is @@ -355,10 +346,10 @@ int useCount = fd.decrementAndGetUseCount(); /* - * If FileDescriptor is still in use by another stream, the finalizer + * If FileDescriptor is still in use by another stream, we * will not close it. */ - if ((useCount <= 0) || !isRunningFinalize()) { + if (useCount <= 0) { close0(); } } @@ -424,18 +415,7 @@ if (fd == FileDescriptor.out || fd == FileDescriptor.err) { flush(); } else { - - /* - * Finalizer should not release the FileDescriptor if another - * stream is still using it. If the user directly invokes - * close() then the FileDescriptor is also released. - */ - runningFinalize.set(Boolean.TRUE); - try { close(); - } finally { - runningFinalize.set(Boolean.FALSE); - } } } } --- old/src/share/classes/java/io/RandomAccessFile.java Thu Sep 8 17:21:24 2011 +++ new/src/share/classes/java/io/RandomAccessFile.java Thu Sep 8 17:21:24 2011 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2011, 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 @@ -590,8 +590,15 @@ * Decrement FD use count associated with this stream. * The count got incremented by FileDescriptor during its construction. */ - fd.decrementAndGetUseCount(); - close0(); + int useCount = fd.decrementAndGetUseCount(); + + /* + * If FileDescriptor is still in use by another stream, we + * will not close it. + */ + if (useCount <= 0) { + close0(); + } } // --- /dev/null Thu Sep 8 17:21:27 2011 +++ new/test/java/io/etc/FileDescriptorSharing.java Thu Sep 8 17:21:26 2011 @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2011, 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 file 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 6322678 7082769 + * @summary FileInputStream(FileDescriptor) throws IOException when reading a file if FD is invalid + * @run main/othervm/timeout=100 FileDescriptorSharing + */ + +import java.io.*; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.util.concurrent.CountDownLatch; + +public class FileDescriptorSharing { + + final static int numFiles = 10; + volatile static boolean fail; + + public static void main(String[] args) throws Exception { + TestFinalizer(); + TestMultipleFD(); + TestIsValid(); + MultiThreadedFD(); + } + + // we shouldn't discard a file descriptor until all streams have + // finished with it + private static void TestFinalizer() throws Exception { + FileInputStream fis1 = null; + FileDescriptor fd = null; + File tempFile = File.createTempFile("Test_", ".txt"); + tempFile.deleteOnExit(); + Writer writer = new FileWriter(tempFile); + for (int i=0; i<10; i++) { + writer.write("test file content test file content"); + } + writer.close(); + + fis1 = new FileInputStream(tempFile); + fd = fis1.getFD(); + /* Create a new FIS based on the existing FD (so the two FIS's share the same native fd) */ + try (FileInputStream fis2 = new FileInputStream(fd)) { + /* allow fis1 to be gc'ed */ + fis1 = null; + int ret = 0; + while(ret >= 0) { + /* encourage gc */ + System.gc(); + /* read from fis2 - when fis1 is gc'ed and finalizer is run, read will fail */ + System.out.print("."); + ret = fis2.read(); + } + } + // variation of above. Use RandomAccessFile to obtain a filedescriptor + RandomAccessFile raf = new RandomAccessFile("Test6322678", "rw"); + raf.writeBytes("test file content could be anything not important just filler"); + fd = raf.getFD(); + fis1 = new FileInputStream(fd); + /* allow raf to be gc'ed */ + raf = null; + int ret = 0; + while(ret >= 0) { + /* encourage gc */ + System.gc(); + /* read from raf - when fis2 is gc'ed and finalizer is run, + * fd should still be valid. + */ + System.out.print("."); + ret = fis1.read(); + } + if(!fd.valid()) { + throw new RuntimeException("Test6322678() : FileDescriptor should be valid"); + } + if(fis1 != null) fis1.close(); + new File("Test6322678").delete(); + + } + + // exercise FileDispatcher close()/preClose() + private static void TestMultipleFD() throws Exception { + RandomAccessFile raf = null; + FileOutputStream fos = null; + FileInputStream fis = null; + FileChannel fc = null; + FileLock fileLock = null; + try { + raf = new RandomAccessFile("test1", "rw"); + fos = new FileOutputStream(raf.getFD()); + fis = new FileInputStream(raf.getFD()); + fc = raf.getChannel(); + fileLock = fc.lock(); + raf.setLength(0L); + fos.flush(); + fos.write("TEST".getBytes()); + } finally { + if (fileLock != null) fileLock.release(); + if (fis != null) fis.close(); + if (fos != null) fos.close(); + if (raf != null) raf.close(); + new File("test1").delete(); + } + // close out in different order to ensure FD is not + // closed out too early + try { + raf = new RandomAccessFile("test2", "rw"); + fos = new FileOutputStream(raf.getFD()); + fis = new FileInputStream(raf.getFD()); + fc = raf.getChannel(); + fileLock = fc.lock(); + raf.setLength(0L); + fos.flush(); + fos.write("TEST".getBytes()); + } finally { + if (fileLock != null) fileLock.release(); + if (raf != null) raf.close(); + new File("test2").delete(); + if (fos != null) fos.close(); + if (fis != null) fis.close(); + } + // one more time, fos first this time + try { + raf = new RandomAccessFile("test3", "rw"); + fos = new FileOutputStream(raf.getFD()); + fis = new FileInputStream(raf.getFD()); + fc = raf.getChannel(); + fileLock = fc.lock(); + raf.setLength(0L); + fos.flush(); + fos.write("TEST".getBytes()); + } finally { + if (fileLock != null) fileLock.release(); + if (fos != null) fos.close(); + if (raf != null) raf.close(); + new File("test3").delete(); + if (fis != null) fis.close(); + } + } + + // Similar to TestMultipleFD() but this time we + // just get and use FileDescriptor.valid() for testing. + private static void TestIsValid() throws Exception { + FileDescriptor fd = null; + RandomAccessFile raf = null; + FileOutputStream fos = null; + FileInputStream fis = null; + FileChannel fc = null; + try { + raf = new RandomAccessFile("test1", "rw"); + fd = raf.getFD(); + fos = new FileOutputStream(fd); + fis = new FileInputStream(fd); + } finally { + if (fis != null) fis.close(); + if (fos != null) fos.close(); + if (!fd.valid()) { + throw new RuntimeException("FileDescriptor should be valid"); + } + if (raf != null) raf.close(); + new File("test1").delete(); + if (fd.valid()) { + throw new RuntimeException("close() called and FileDescriptor still valid"); + } + } + // close out in different order to ensure FD is not + // closed out too early + try { + raf = new RandomAccessFile("test2", "rw"); + fd = raf.getFD(); + fos = new FileOutputStream(fd); + fis = new FileInputStream(fd); + } finally { + if (raf != null) raf.close(); + new File("test2").delete(); + if (fos != null) fos.close(); + if (!fd.valid()) { + throw new RuntimeException("FileDescriptor should be valid"); + } + if (fis != null) fis.close(); + if (fd.valid()) { + throw new RuntimeException("close() called and FileDescriptor still valid"); + } + } + // one more time, fos first this time + try { + raf = new RandomAccessFile("test3", "rw"); + fd = raf.getFD(); + fos = new FileOutputStream(fd); + fis = new FileInputStream(fd); + } finally { + if (fos != null) fos.close(); + if (raf != null) raf.close(); + new File("test3").delete(); + if (!fd.valid()) { + throw new RuntimeException("FileDescriptor should be valid"); + } + if (fis != null) fis.close(); + if (fd.valid()) { + throw new RuntimeException("close() called and FileDescriptor still valid"); + } + } + } + + // Test concurrent access to the same fd.useCount field + private static void MultiThreadedFD() throws Exception { + RandomAccessFile raf = null; + FileDescriptor fd = null; + int numThreads = 2; + CountDownLatch done = new CountDownLatch(numThreads); + OpenClose[] fileOpenClose = new OpenClose[numThreads]; + try { + raf = new RandomAccessFile("MultipleThreadedFD", "rw"); + fd = raf.getFD(); + for(int count=0;count