test/jdk/java/io/FileDescriptor/Sharing.java
changeset 47216 71c04702a3d5
parent 32649 2ee9017c7597
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 /*
       
    25  * @test
       
    26  * @bug 7105952 6322678 7082769
       
    27  * @summary Improve finalisation for FileInputStream/FileOutputStream/RandomAccessFile
       
    28  * @run main/othervm Sharing
       
    29  */
       
    30 
       
    31 import java.io.*;
       
    32 import java.nio.channels.FileChannel;
       
    33 import java.nio.channels.FileLock;
       
    34 import java.util.concurrent.CountDownLatch;
       
    35 
       
    36 public class Sharing {
       
    37 
       
    38     static final int numFiles = 10;
       
    39     static volatile boolean fail;
       
    40 
       
    41     public static void main(String[] args) throws Exception {
       
    42         TestFinalizer();
       
    43         TestMultipleFD();
       
    44         TestIsValid();
       
    45         MultiThreadedFD();
       
    46         TestCloseAll();
       
    47     }
       
    48 
       
    49     /**
       
    50      * Finalizer shouldn't discard a file descriptor until all streams have
       
    51      * finished with it.
       
    52      */
       
    53     private static void TestFinalizer() throws Exception {
       
    54         FileDescriptor fd = null;
       
    55         File tempFile = new File("TestFinalizer1.txt");
       
    56         tempFile.deleteOnExit();
       
    57         try (Writer writer = new FileWriter(tempFile)) {
       
    58             for (int i=0; i<5; i++) {
       
    59                 writer.write("test file content test file content");
       
    60             }
       
    61         }
       
    62 
       
    63         FileInputStream fis1 = new FileInputStream(tempFile);
       
    64         fd = fis1.getFD();
       
    65         // Create a new FIS based on the existing FD (so the two FIS's share the same native fd)
       
    66         try (FileInputStream fis2 = new FileInputStream(fd)) {
       
    67             // allow fis1 to be gc'ed
       
    68             fis1 = null;
       
    69             int ret = 0;
       
    70             while(ret >= 0) {
       
    71                 // encourage gc
       
    72                 System.gc();
       
    73                 // read from fis2 - when fis1 is gc'ed and finalizer is run, read will fail
       
    74                 System.out.print(".");
       
    75                 ret = fis2.read();
       
    76             }
       
    77         }
       
    78 
       
    79         // variation of above. Use RandomAccessFile to obtain a filedescriptor
       
    80         File testFinalizerFile = new File("TestFinalizer");
       
    81         RandomAccessFile raf = new RandomAccessFile(testFinalizerFile, "rw");
       
    82         raf.writeBytes("test file content test file content");
       
    83         raf.seek(0L);
       
    84         fd = raf.getFD();
       
    85         try (FileInputStream fis3 = new FileInputStream(fd)) {
       
    86             // allow raf to be gc'ed
       
    87             raf = null;
       
    88             int ret = 0;
       
    89             while (ret >= 0) {
       
    90                 // encourage gc
       
    91                 System.gc();
       
    92                 /*
       
    93                  * read from fis3 - when raf is gc'ed and finalizer is run,
       
    94                  * fd should still be valid.
       
    95                  */
       
    96                 System.out.print(".");
       
    97                 ret = fis3.read();
       
    98             }
       
    99         } finally {
       
   100             testFinalizerFile.delete();
       
   101         }
       
   102     }
       
   103 
       
   104     /**
       
   105      * Exercise FileDispatcher close()/preClose()
       
   106      */
       
   107     private static void TestMultipleFD() throws Exception {
       
   108         RandomAccessFile raf = null;
       
   109         FileOutputStream fos = null;
       
   110         FileInputStream fis = null;
       
   111         FileChannel fc = null;
       
   112         FileLock fileLock = null;
       
   113 
       
   114         File test1 = new File("test1");
       
   115         try {
       
   116             raf = new RandomAccessFile(test1, "rw");
       
   117             fos = new FileOutputStream(raf.getFD());
       
   118             fis = new FileInputStream(raf.getFD());
       
   119             fc = raf.getChannel();
       
   120             fileLock = fc.lock();
       
   121             raf.setLength(0L);
       
   122             fos.flush();
       
   123             fos.write("TEST".getBytes());
       
   124         } finally {
       
   125             if (fileLock != null) fileLock.release();
       
   126             if (fis != null) fis.close();
       
   127             if (fos != null) fos.close();
       
   128             if (raf != null) raf.close();
       
   129             test1.delete();
       
   130         }
       
   131 
       
   132         /*
       
   133          * Close out in different order to ensure FD is not
       
   134          * closed out too early
       
   135          */
       
   136         File test2 = new File("test2");
       
   137         try {
       
   138             raf = new RandomAccessFile(test2, "rw");
       
   139             fos = new FileOutputStream(raf.getFD());
       
   140             fis = new FileInputStream(raf.getFD());
       
   141             fc = raf.getChannel();
       
   142             fileLock = fc.lock();
       
   143             raf.setLength(0L);
       
   144             fos.flush();
       
   145             fos.write("TEST".getBytes());
       
   146         } finally {
       
   147             if (fileLock != null) fileLock.release();
       
   148             if (raf != null) raf.close();
       
   149             if (fos != null) fos.close();
       
   150             if (fis != null) fis.close();
       
   151             test2.delete();
       
   152         }
       
   153 
       
   154         // one more time, fos first this time
       
   155         File test3 = new File("test3");
       
   156         try {
       
   157             raf = new RandomAccessFile(test3, "rw");
       
   158             fos = new FileOutputStream(raf.getFD());
       
   159             fis = new FileInputStream(raf.getFD());
       
   160             fc = raf.getChannel();
       
   161             fileLock = fc.lock();
       
   162             raf.setLength(0L);
       
   163             fos.flush();
       
   164             fos.write("TEST".getBytes());
       
   165         } finally {
       
   166             if (fileLock != null) fileLock.release();
       
   167             if (fos != null) fos.close();
       
   168             if (raf != null) raf.close();
       
   169             if (fis != null) fis.close();
       
   170             test3.delete();
       
   171         }
       
   172     }
       
   173 
       
   174     /**
       
   175      * Similar to TestMultipleFD() but this time we
       
   176      * just get and use FileDescriptor.valid() for testing.
       
   177      */
       
   178     private static void TestIsValid() throws Exception {
       
   179         FileDescriptor fd = null;
       
   180         RandomAccessFile raf = null;
       
   181         FileOutputStream fos = null;
       
   182         FileInputStream fis = null;
       
   183         FileChannel fc = null;
       
   184 
       
   185         File test1 = new File("test1");
       
   186         try {
       
   187             raf = new RandomAccessFile(test1, "rw");
       
   188             fd = raf.getFD();
       
   189             fos = new FileOutputStream(fd);
       
   190             fis = new FileInputStream(fd);
       
   191         } finally {
       
   192             try {
       
   193                 if (fis != null) fis.close();
       
   194                 if (fd.valid()) {
       
   195                     throw new RuntimeException("[FIS close()] FileDescriptor shouldn't be valid");
       
   196                 }
       
   197                 if (fos != null) fos.close();
       
   198                 if (raf != null) raf.close();
       
   199             } finally {
       
   200                 test1.delete();
       
   201             }
       
   202         }
       
   203 
       
   204         /*
       
   205          * Close out in different order to ensure FD is
       
   206          * closed correctly.
       
   207          */
       
   208         File test2 = new File("test2");
       
   209         try {
       
   210             raf = new RandomAccessFile(test2, "rw");
       
   211             fd = raf.getFD();
       
   212             fos = new FileOutputStream(fd);
       
   213             fis = new FileInputStream(fd);
       
   214         } finally {
       
   215             try {
       
   216                 if (raf != null) raf.close();
       
   217                 if (fd.valid()) {
       
   218                     throw new RuntimeException("[RAF close()] FileDescriptor shouldn't be valid");
       
   219                 }
       
   220                 if (fos != null) fos.close();
       
   221                 if (fis != null) fis.close();
       
   222             } finally {
       
   223                 test2.delete();
       
   224             }
       
   225         }
       
   226 
       
   227         // one more time, fos first this time
       
   228         File test3 = new File("test3");
       
   229         try {
       
   230             raf = new RandomAccessFile(test3, "rw");
       
   231             fd = raf.getFD();
       
   232             fos = new FileOutputStream(fd);
       
   233             fis = new FileInputStream(fd);
       
   234         } finally {
       
   235             try {
       
   236                 if (fos != null) fos.close();
       
   237                 if (fd.valid()) {
       
   238                     throw new RuntimeException("[FOS close()] FileDescriptor shouldn't be valid");
       
   239                 }
       
   240                 if (raf != null) raf.close();
       
   241                 if (fis != null) fis.close();
       
   242             } finally {
       
   243                 test3.delete();
       
   244             }
       
   245         }
       
   246     }
       
   247 
       
   248     /**
       
   249      * Test concurrent access to the same FileDescriptor
       
   250      */
       
   251     private static void MultiThreadedFD() throws Exception {
       
   252         RandomAccessFile raf = null;
       
   253         FileDescriptor fd = null;
       
   254         int numThreads = 2;
       
   255         CountDownLatch done = new CountDownLatch(numThreads);
       
   256         OpenClose[] fileOpenClose = new OpenClose[numThreads];
       
   257         File MultipleThreadedFD = new File("MultipleThreadedFD");
       
   258         try {
       
   259             raf = new RandomAccessFile(MultipleThreadedFD, "rw");
       
   260             fd = raf.getFD();
       
   261             for(int count=0;count<numThreads;count++) {
       
   262                 fileOpenClose[count] = new OpenClose(fd, done);
       
   263                 fileOpenClose[count].start();
       
   264             }
       
   265             done.await();
       
   266         } finally {
       
   267             try {
       
   268                 if(raf != null) raf.close();
       
   269                 // fd should now no longer be valid
       
   270                 if(fd.valid()) {
       
   271                     throw new RuntimeException("FileDescriptor should not be valid");
       
   272                 }
       
   273                 // OpenClose thread tests failed
       
   274                 if(fail) {
       
   275                     throw new RuntimeException("OpenClose thread tests failed.");
       
   276                 }
       
   277             } finally {
       
   278                 MultipleThreadedFD.delete();
       
   279             }
       
   280         }
       
   281     }
       
   282 
       
   283     /**
       
   284      * Test closeAll handling in FileDescriptor
       
   285      */
       
   286     private static void TestCloseAll() throws Exception {
       
   287         File testFile = new File("test");
       
   288         testFile.deleteOnExit();
       
   289         RandomAccessFile raf = new RandomAccessFile(testFile, "rw");
       
   290         FileInputStream fis = new FileInputStream(raf.getFD());
       
   291         fis.close();
       
   292         if (raf.getFD().valid()) {
       
   293              throw new RuntimeException("FD should not be valid.");
       
   294         }
       
   295 
       
   296         // Test the suppressed exception handling - FileInputStream
       
   297 
       
   298         raf = new RandomAccessFile(testFile, "rw");
       
   299         fis = new FileInputStream(raf.getFD());
       
   300         BadFileInputStream bfis1 = new BadFileInputStream(raf.getFD());
       
   301         BadFileInputStream bfis2 = new BadFileInputStream(raf.getFD());
       
   302         BadFileInputStream bfis3 = new BadFileInputStream(raf.getFD());
       
   303         // extra test - set bfis3 to null
       
   304         bfis3 = null;
       
   305         try {
       
   306             fis.close();
       
   307         } catch (IOException ioe) {
       
   308             ioe.printStackTrace();
       
   309             if (ioe.getSuppressed().length != 2) {
       
   310                 throw new RuntimeException("[FIS]Incorrect number of suppressed " +
       
   311                           "exceptions received : " + ioe.getSuppressed().length);
       
   312             }
       
   313         }
       
   314         if (raf.getFD().valid()) {
       
   315             // we should still have closed the FD
       
   316             // even with the exception.
       
   317             throw new RuntimeException("[FIS]TestCloseAll : FD still valid.");
       
   318         }
       
   319 
       
   320         // Now test with FileOutputStream
       
   321 
       
   322         raf = new RandomAccessFile(testFile, "rw");
       
   323         FileOutputStream fos = new FileOutputStream(raf.getFD());
       
   324         BadFileOutputStream bfos1 = new BadFileOutputStream(raf.getFD());
       
   325         BadFileOutputStream bfos2 = new BadFileOutputStream(raf.getFD());
       
   326         BadFileOutputStream bfos3 = new BadFileOutputStream(raf.getFD());
       
   327         // extra test - set bfos3 to null
       
   328         bfos3 = null;
       
   329         try {
       
   330             fos.close();
       
   331         } catch (IOException ioe) {
       
   332             ioe.printStackTrace();
       
   333             if (ioe.getSuppressed().length != 2) {
       
   334                 throw new RuntimeException("[FOS]Incorrect number of suppressed " +
       
   335                           "exceptions received : " + ioe.getSuppressed().length);
       
   336             }
       
   337         }
       
   338         if (raf.getFD().valid()) {
       
   339             // we should still have closed the FD
       
   340             // even with the exception.
       
   341             throw new RuntimeException("[FOS]TestCloseAll : FD still valid.");
       
   342         }
       
   343     }
       
   344 
       
   345     /**
       
   346      * A thread which will open and close a number of FileInputStreams and
       
   347      * FileOutputStreams referencing the same native file descriptor.
       
   348      */
       
   349     private static class OpenClose extends Thread {
       
   350         private FileDescriptor fd = null;
       
   351         private CountDownLatch done;
       
   352         FileInputStream[] fisArray = new FileInputStream[numFiles];
       
   353         FileOutputStream[] fosArray = new FileOutputStream[numFiles];
       
   354 
       
   355         OpenClose(FileDescriptor filedescriptor, CountDownLatch done) {
       
   356             this.fd = filedescriptor;
       
   357             this.done = done;
       
   358         }
       
   359 
       
   360         public void run() {
       
   361              try {
       
   362                  for(int i=0;i<numFiles;i++) {
       
   363                      fisArray[i] = new FileInputStream(fd);
       
   364                      fosArray[i] = new FileOutputStream(fd);
       
   365                  }
       
   366 
       
   367                  // Now close out
       
   368                  for(int i=0;i<numFiles;i++) {
       
   369                      if(fisArray[i] != null) fisArray[i].close();
       
   370                      if(fosArray[i] != null) fosArray[i].close();
       
   371                  }
       
   372 
       
   373              } catch(IOException ioe) {
       
   374                  System.out.println("OpenClose encountered IO issue :" + ioe);
       
   375                  fail = true;
       
   376              } finally {
       
   377                  if (fd.valid()) { // fd should not be valid after first close() call
       
   378                      System.out.println("OpenClose: FileDescriptor shouldn't be valid");
       
   379                      fail = true;
       
   380                  }
       
   381                  done.countDown();
       
   382              }
       
   383          }
       
   384     }
       
   385 
       
   386     private static class BadFileInputStream extends FileInputStream {
       
   387 
       
   388         BadFileInputStream(FileDescriptor fd) {
       
   389             super(fd);
       
   390         }
       
   391 
       
   392         public void close() throws IOException {
       
   393             throw new IOException("Bad close operation");
       
   394         }
       
   395     }
       
   396 
       
   397     private static class BadFileOutputStream extends FileOutputStream {
       
   398 
       
   399         BadFileOutputStream(FileDescriptor fd) {
       
   400             super(fd);
       
   401         }
       
   402 
       
   403         public void close() throws IOException {
       
   404             throw new IOException("Bad close operation");
       
   405         }
       
   406     }
       
   407 
       
   408 }