| /* |
| * 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 7105952 6322678 7082769 |
| * @summary Improve finalisation for FileInputStream/FileOutputStream/RandomAccessFile |
| * @run main/othervm Sharing |
| */ |
| |
| import java.io.*; |
| import java.nio.channels.FileChannel; |
| import java.nio.channels.FileLock; |
| import java.util.concurrent.CountDownLatch; |
| |
| public class Sharing { |
| |
| static final int numFiles = 10; |
| static volatile boolean fail; |
| |
| public static void main(String[] args) throws Exception { |
| TestFinalizer(); |
| TestMultipleFD(); |
| TestIsValid(); |
| MultiThreadedFD(); |
| TestCloseAll(); |
| } |
| |
| /** |
| * Finalizer shouldn't discard a file descriptor until all streams have |
| * finished with it. |
| */ |
| private static void TestFinalizer() throws Exception { |
| FileDescriptor fd = null; |
| File tempFile = new File("TestFinalizer1.txt"); |
| tempFile.deleteOnExit(); |
| try (Writer writer = new FileWriter(tempFile)) { |
| for (int i=0; i<5; i++) { |
| writer.write("test file content test file content"); |
| } |
| } |
| |
| FileInputStream 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 |
| File testFinalizerFile = new File("TestFinalizer"); |
| RandomAccessFile raf = new RandomAccessFile(testFinalizerFile, "rw"); |
| raf.writeBytes("test file content test file content"); |
| raf.seek(0L); |
| fd = raf.getFD(); |
| try (FileInputStream fis3 = new FileInputStream(fd)) { |
| // allow raf to be gc'ed |
| raf = null; |
| int ret = 0; |
| while (ret >= 0) { |
| // encourage gc |
| System.gc(); |
| /* |
| * read from fis3 - when raf is gc'ed and finalizer is run, |
| * fd should still be valid. |
| */ |
| System.out.print("."); |
| ret = fis3.read(); |
| } |
| } finally { |
| testFinalizerFile.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; |
| |
| File test1 = new File("test1"); |
| 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(); |
| test1.delete(); |
| } |
| |
| /* |
| * Close out in different order to ensure FD is not |
| * closed out too early |
| */ |
| File test2 = new File("test2"); |
| 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(); |
| if (fos != null) fos.close(); |
| if (fis != null) fis.close(); |
| test2.delete(); |
| } |
| |
| // one more time, fos first this time |
| File test3 = new File("test3"); |
| 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(); |
| if (fis != null) fis.close(); |
| test3.delete(); |
| } |
| } |
| |
| /** |
| * 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; |
| |
| File test1 = new File("test1"); |
| try { |
| raf = new RandomAccessFile(test1, "rw"); |
| fd = raf.getFD(); |
| fos = new FileOutputStream(fd); |
| fis = new FileInputStream(fd); |
| } finally { |
| try { |
| if (fis != null) fis.close(); |
| if (fd.valid()) { |
| throw new RuntimeException("[FIS close()] FileDescriptor shouldn't be valid"); |
| } |
| if (fos != null) fos.close(); |
| if (raf != null) raf.close(); |
| } finally { |
| test1.delete(); |
| } |
| } |
| |
| /* |
| * Close out in different order to ensure FD is |
| * closed correctly. |
| */ |
| File test2 = new File("test2"); |
| try { |
| raf = new RandomAccessFile(test2, "rw"); |
| fd = raf.getFD(); |
| fos = new FileOutputStream(fd); |
| fis = new FileInputStream(fd); |
| } finally { |
| try { |
| if (raf != null) raf.close(); |
| if (fd.valid()) { |
| throw new RuntimeException("[RAF close()] FileDescriptor shouldn't be valid"); |
| } |
| if (fos != null) fos.close(); |
| if (fis != null) fis.close(); |
| } finally { |
| test2.delete(); |
| } |
| } |
| |
| // one more time, fos first this time |
| File test3 = new File("test3"); |
| try { |
| raf = new RandomAccessFile(test3, "rw"); |
| fd = raf.getFD(); |
| fos = new FileOutputStream(fd); |
| fis = new FileInputStream(fd); |
| } finally { |
| try { |
| if (fos != null) fos.close(); |
| if (fd.valid()) { |
| throw new RuntimeException("[FOS close()] FileDescriptor shouldn't be valid"); |
| } |
| if (raf != null) raf.close(); |
| if (fis != null) fis.close(); |
| } finally { |
| test3.delete(); |
| } |
| } |
| } |
| |
| /** |
| * Test concurrent access to the same FileDescriptor |
| */ |
| 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]; |
| File MultipleThreadedFD = new File("MultipleThreadedFD"); |
| try { |
| raf = new RandomAccessFile(MultipleThreadedFD, "rw"); |
| fd = raf.getFD(); |
| for(int count=0;count<numThreads;count++) { |
| fileOpenClose[count] = new OpenClose(fd, done); |
| fileOpenClose[count].start(); |
| } |
| done.await(); |
| } finally { |
| try { |
| if(raf != null) raf.close(); |
| // fd should now no longer be valid |
| if(fd.valid()) { |
| throw new RuntimeException("FileDescriptor should not be valid"); |
| } |
| // OpenClose thread tests failed |
| if(fail) { |
| throw new RuntimeException("OpenClose thread tests failed."); |
| } |
| } finally { |
| MultipleThreadedFD.delete(); |
| } |
| } |
| } |
| |
| /** |
| * Test closeAll handling in FileDescriptor |
| */ |
| private static void TestCloseAll() throws Exception { |
| File testFile = new File("test"); |
| testFile.deleteOnExit(); |
| RandomAccessFile raf = new RandomAccessFile(testFile, "rw"); |
| FileInputStream fis = new FileInputStream(raf.getFD()); |
| fis.close(); |
| if (raf.getFD().valid()) { |
| throw new RuntimeException("FD should not be valid."); |
| } |
| |
| // Test the suppressed exception handling - FileInputStream |
| |
| raf = new RandomAccessFile(testFile, "rw"); |
| fis = new FileInputStream(raf.getFD()); |
| BadFileInputStream bfis1 = new BadFileInputStream(raf.getFD()); |
| BadFileInputStream bfis2 = new BadFileInputStream(raf.getFD()); |
| BadFileInputStream bfis3 = new BadFileInputStream(raf.getFD()); |
| // extra test - set bfis3 to null |
| bfis3 = null; |
| try { |
| fis.close(); |
| } catch (IOException ioe) { |
| ioe.printStackTrace(); |
| if (ioe.getSuppressed().length != 2) { |
| throw new RuntimeException("[FIS]Incorrect number of suppressed " + |
| "exceptions received : " + ioe.getSuppressed().length); |
| } |
| } |
| if (raf.getFD().valid()) { |
| // we should still have closed the FD |
| // even with the exception. |
| throw new RuntimeException("[FIS]TestCloseAll : FD still valid."); |
| } |
| |
| // Now test with FileOutputStream |
| |
| raf = new RandomAccessFile(testFile, "rw"); |
| FileOutputStream fos = new FileOutputStream(raf.getFD()); |
| BadFileOutputStream bfos1 = new BadFileOutputStream(raf.getFD()); |
| BadFileOutputStream bfos2 = new BadFileOutputStream(raf.getFD()); |
| BadFileOutputStream bfos3 = new BadFileOutputStream(raf.getFD()); |
| // extra test - set bfos3 to null |
| bfos3 = null; |
| try { |
| fos.close(); |
| } catch (IOException ioe) { |
| ioe.printStackTrace(); |
| if (ioe.getSuppressed().length != 2) { |
| throw new RuntimeException("[FOS]Incorrect number of suppressed " + |
| "exceptions received : " + ioe.getSuppressed().length); |
| } |
| } |
| if (raf.getFD().valid()) { |
| // we should still have closed the FD |
| // even with the exception. |
| throw new RuntimeException("[FOS]TestCloseAll : FD still valid."); |
| } |
| } |
| |
| /** |
| * A thread which will open and close a number of FileInputStreams and |
| * FileOutputStreams referencing the same native file descriptor. |
| */ |
| private static class OpenClose extends Thread { |
| private FileDescriptor fd = null; |
| private CountDownLatch done; |
| FileInputStream[] fisArray = new FileInputStream[numFiles]; |
| FileOutputStream[] fosArray = new FileOutputStream[numFiles]; |
| |
| OpenClose(FileDescriptor filedescriptor, CountDownLatch done) { |
| this.fd = filedescriptor; |
| this.done = done; |
| } |
| |
| public void run() { |
| try { |
| for(int i=0;i<numFiles;i++) { |
| fisArray[i] = new FileInputStream(fd); |
| fosArray[i] = new FileOutputStream(fd); |
| } |
| |
| // Now close out |
| for(int i=0;i<numFiles;i++) { |
| if(fisArray[i] != null) fisArray[i].close(); |
| if(fosArray[i] != null) fosArray[i].close(); |
| } |
| |
| } catch(IOException ioe) { |
| System.out.println("OpenClose encountered IO issue :" + ioe); |
| fail = true; |
| } finally { |
| if (fd.valid()) { // fd should not be valid after first close() call |
| System.out.println("OpenClose: FileDescriptor shouldn't be valid"); |
| fail = true; |
| } |
| done.countDown(); |
| } |
| } |
| } |
| |
| private static class BadFileInputStream extends FileInputStream { |
| |
| BadFileInputStream(FileDescriptor fd) { |
| super(fd); |
| } |
| |
| public void close() throws IOException { |
| throw new IOException("Bad close operation"); |
| } |
| } |
| |
| private static class BadFileOutputStream extends FileOutputStream { |
| |
| BadFileOutputStream(FileDescriptor fd) { |
| super(fd); |
| } |
| |
| public void close() throws IOException { |
| throw new IOException("Bad close operation"); |
| } |
| } |
| |
| } |