jdk/test/java/io/etc/FileDescriptorSharing.java
changeset 11041 64fbc4786179
parent 11008 cc30ba2655b8
parent 11040 bba8493895c2
child 11042 465818f87eea
equal deleted inserted replaced
11008:cc30ba2655b8 11041:64fbc4786179
     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 6322678 7082769
       
    27  * @summary FileInputStream/FileOutputStream/RandomAccessFile allow file descriptor
       
    28  *          to be closed while still in use.
       
    29  * @run main/othervm FileDescriptorSharing
       
    30  */
       
    31 
       
    32 import java.io.*;
       
    33 import java.nio.channels.FileChannel;
       
    34 import java.nio.channels.FileLock;
       
    35 import java.util.concurrent.CountDownLatch;
       
    36 
       
    37 public class FileDescriptorSharing {
       
    38 
       
    39     final static int numFiles = 10;
       
    40     volatile static boolean fail;
       
    41 
       
    42     public static void main(String[] args) throws Exception {
       
    43         TestFinalizer();
       
    44         TestMultipleFD();
       
    45         TestIsValid();
       
    46         MultiThreadedFD();
       
    47     }
       
    48 
       
    49     /**
       
    50      * We 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             if(!fd.valid()) {
       
   100                 throw new RuntimeException("TestFinalizer() : FileDescriptor should be valid");
       
   101             }
       
   102         } finally {
       
   103             testFinalizerFile.delete();
       
   104         }
       
   105     }
       
   106 
       
   107     /**
       
   108      * Exercise FileDispatcher close()/preClose()
       
   109      */
       
   110     private static void TestMultipleFD() throws Exception {
       
   111         RandomAccessFile raf = null;
       
   112         FileOutputStream fos = null;
       
   113         FileInputStream fis = null;
       
   114         FileChannel fc = null;
       
   115         FileLock fileLock = null;
       
   116 
       
   117         File test1 = new File("test1");
       
   118         try {
       
   119             raf = new RandomAccessFile(test1, "rw");
       
   120             fos = new FileOutputStream(raf.getFD());
       
   121             fis = new FileInputStream(raf.getFD());
       
   122             fc = raf.getChannel();
       
   123             fileLock = fc.lock();
       
   124             raf.setLength(0L);
       
   125             fos.flush();
       
   126             fos.write("TEST".getBytes());
       
   127         } finally {
       
   128             if (fileLock != null) fileLock.release();
       
   129             if (fis != null) fis.close();
       
   130             if (fos != null) fos.close();
       
   131             if (raf != null) raf.close();
       
   132             test1.delete();
       
   133         }
       
   134 
       
   135         /*
       
   136          * Close out in different order to ensure FD is not
       
   137          * closed out too early
       
   138          */
       
   139         File test2 = new File("test2");
       
   140         try {
       
   141             raf = new RandomAccessFile(test2, "rw");
       
   142             fos = new FileOutputStream(raf.getFD());
       
   143             fis = new FileInputStream(raf.getFD());
       
   144             fc = raf.getChannel();
       
   145             fileLock = fc.lock();
       
   146             raf.setLength(0L);
       
   147             fos.flush();
       
   148             fos.write("TEST".getBytes());
       
   149         } finally {
       
   150             if (fileLock != null) fileLock.release();
       
   151             if (raf != null) raf.close();
       
   152             if (fos != null) fos.close();
       
   153             if (fis != null) fis.close();
       
   154             test2.delete();
       
   155         }
       
   156 
       
   157         // one more time, fos first this time
       
   158         File test3 = new File("test3");
       
   159         try {
       
   160             raf = new RandomAccessFile(test3, "rw");
       
   161             fos = new FileOutputStream(raf.getFD());
       
   162             fis = new FileInputStream(raf.getFD());
       
   163             fc = raf.getChannel();
       
   164             fileLock = fc.lock();
       
   165             raf.setLength(0L);
       
   166             fos.flush();
       
   167             fos.write("TEST".getBytes());
       
   168         } finally {
       
   169             if (fileLock != null) fileLock.release();
       
   170             if (fos != null) fos.close();
       
   171             if (raf != null) raf.close();
       
   172             if (fis != null) fis.close();
       
   173             test3.delete();
       
   174         }
       
   175     }
       
   176 
       
   177     /**
       
   178      * Similar to TestMultipleFD() but this time we
       
   179      * just get and use FileDescriptor.valid() for testing.
       
   180      */
       
   181     private static void TestIsValid() throws Exception {
       
   182         FileDescriptor fd = null;
       
   183         RandomAccessFile raf = null;
       
   184         FileOutputStream fos = null;
       
   185         FileInputStream fis = null;
       
   186         FileChannel fc = null;
       
   187 
       
   188         File test1 = new File("test1");
       
   189         try {
       
   190             raf = new RandomAccessFile(test1, "rw");
       
   191             fd = raf.getFD();
       
   192             fos = new FileOutputStream(fd);
       
   193             fis = new FileInputStream(fd);
       
   194         } finally {
       
   195             try {
       
   196                 if (fis != null) fis.close();
       
   197                 if (fos != null) fos.close();
       
   198                 if (!fd.valid()) {
       
   199                     throw new RuntimeException("FileDescriptor should be valid");
       
   200                 }
       
   201                 if (raf != null) raf.close();
       
   202                 if (fd.valid()) {
       
   203                     throw new RuntimeException("close() called and FileDescriptor still valid");
       
   204                 }
       
   205             } finally {
       
   206                 if (raf != null) raf.close();
       
   207                 test1.delete();
       
   208             }
       
   209         }
       
   210 
       
   211         /*
       
   212          * Close out in different order to ensure FD is not
       
   213          * closed out too early
       
   214          */
       
   215         File test2 = new File("test2");
       
   216         try {
       
   217             raf = new RandomAccessFile(test2, "rw");
       
   218             fd = raf.getFD();
       
   219             fos = new FileOutputStream(fd);
       
   220             fis = new FileInputStream(fd);
       
   221         } finally {
       
   222             try {
       
   223                 if (raf != null) raf.close();
       
   224                 if (fos != null) fos.close();
       
   225                 if (!fd.valid()) {
       
   226                     throw new RuntimeException("FileDescriptor should be valid");
       
   227                 }
       
   228                 if (fis != null) fis.close();
       
   229                 if (fd.valid()) {
       
   230                     throw new RuntimeException("close() called and FileDescriptor still valid");
       
   231                 }
       
   232             } finally {
       
   233                 test2.delete();
       
   234             }
       
   235         }
       
   236 
       
   237         // one more time, fos first this time
       
   238         File test3 = new File("test3");
       
   239         try {
       
   240             raf = new RandomAccessFile(test3, "rw");
       
   241             fd = raf.getFD();
       
   242             fos = new FileOutputStream(fd);
       
   243             fis = new FileInputStream(fd);
       
   244         } finally {
       
   245             try {
       
   246                 if (fos != null) fos.close();
       
   247                 if (raf != null) raf.close();
       
   248                 if (!fd.valid()) {
       
   249                     throw new RuntimeException("FileDescriptor should be valid");
       
   250                 }
       
   251                 if (fis != null) fis.close();
       
   252                 if (fd.valid()) {
       
   253                     throw new RuntimeException("close() called and FileDescriptor still valid");
       
   254                 }
       
   255             } finally {
       
   256                 test3.delete();
       
   257             }
       
   258         }
       
   259     }
       
   260 
       
   261     /**
       
   262      * Test concurrent access to the same fd.useCount field
       
   263      */
       
   264     private static void MultiThreadedFD() throws Exception {
       
   265         RandomAccessFile raf = null;
       
   266         FileDescriptor fd = null;
       
   267         int numThreads = 2;
       
   268         CountDownLatch done = new CountDownLatch(numThreads);
       
   269         OpenClose[] fileOpenClose = new OpenClose[numThreads];
       
   270         File MultipleThreadedFD = new File("MultipleThreadedFD");
       
   271         try {
       
   272             raf = new RandomAccessFile(MultipleThreadedFD, "rw");
       
   273             fd = raf.getFD();
       
   274             for(int count=0;count<numThreads;count++) {
       
   275                 fileOpenClose[count] = new OpenClose(fd, done);
       
   276                 fileOpenClose[count].start();
       
   277             }
       
   278             done.await();
       
   279         } finally {
       
   280             try {
       
   281                 if(raf != null) raf.close();
       
   282                 // fd should now no longer be valid
       
   283                 if(fd.valid()) {
       
   284                     throw new RuntimeException("FileDescriptor should not be valid");
       
   285                 }
       
   286                 // OpenClose thread tests failed
       
   287                 if(fail) {
       
   288                     throw new RuntimeException("OpenClose thread tests failed.");
       
   289                 }
       
   290             } finally {
       
   291                 MultipleThreadedFD.delete();
       
   292             }
       
   293         }
       
   294     }
       
   295 
       
   296     /**
       
   297      * A thread which will open and close a number of FileInputStreams and
       
   298      * FileOutputStreams referencing the same native file descriptor.
       
   299      */
       
   300     private static class OpenClose extends Thread {
       
   301         private FileDescriptor fd = null;
       
   302         private CountDownLatch done;
       
   303         FileInputStream[] fisArray = new FileInputStream[numFiles];
       
   304         FileOutputStream[] fosArray = new FileOutputStream[numFiles];
       
   305 
       
   306         OpenClose(FileDescriptor filedescriptor, CountDownLatch done) {
       
   307             this.fd = filedescriptor;
       
   308             this.done = done;
       
   309         }
       
   310 
       
   311         public void run() {
       
   312              try {
       
   313                  for(int i=0;i<numFiles;i++) {
       
   314                      fisArray[i] = new FileInputStream(fd);
       
   315                      fosArray[i] = new FileOutputStream(fd);
       
   316                  }
       
   317 
       
   318                  // Now close out
       
   319                  for(int i=0;i<numFiles;i++) {
       
   320                      if(fisArray[i] != null) fisArray[i].close();
       
   321                      if(fosArray[i] != null) fosArray[i].close();
       
   322                  }
       
   323 
       
   324              } catch(IOException ioe) {
       
   325                  System.out.println("OpenClose encountered IO issue :" + ioe);
       
   326                  fail = true;
       
   327              } finally {
       
   328                  if (!fd.valid()) { // fd should still be valid given RAF reference
       
   329                      System.out.println("OpenClose: FileDescriptor should be valid");
       
   330                      fail = true;
       
   331                  }
       
   332                  done.countDown();
       
   333              }
       
   334          }
       
   335     }
       
   336 }