src/share/classes/java/io/FileInputStream.java

Print this page

        

@@ -54,10 +54,20 @@
     private FileChannel channel = null;
 
     private final Object closeLock = new Object();
     private volatile boolean closed = false;
 
+    private static final ThreadLocal<Boolean> runningFinalize =
+        new ThreadLocal<>();
+
+    private static boolean isRunningFinalize() {
+        Boolean val;
+        if ((val = runningFinalize.get()) != null)
+            return val.booleanValue();
+        return false;
+    }
+
     /**
      * Creates a <code>FileInputStream</code> by
      * opening a connection to an actual file,
      * the file named by the path name <code>name</code>
      * in the file system.  A new <code>FileDescriptor</code>

@@ -122,11 +132,11 @@
         }
         if (name == null) {
             throw new NullPointerException();
         }
         fd = new FileDescriptor();
-        fd.attach(this);
+        fd.incrementAndGetUseCount();
         open(name);
     }
 
     /**
      * Creates a <code>FileInputStream</code> by using the file descriptor

@@ -162,13 +172,14 @@
         }
         fd = fdObj;
 
         /*
          * FileDescriptor is being shared by streams.
-         * Register this stream with FileDescriptor tracker.
+         * Ensure that it's GC'ed only when all the streams/channels are done
+         * using it.
          */
-        fd.attach(this);
+        fd.incrementAndGetUseCount();
     }
 
     /**
      * Opens the specified file for reading.
      * @param name the name of the file

@@ -291,17 +302,31 @@
                 return;
             }
             closed = true;
         }
         if (channel != null) {
+            /*
+             * Decrement the FD use count associated with the channel
+             * The use count is incremented whenever a new channel
+             * is obtained from this stream.
+             */
+           fd.decrementAndGetUseCount();
            channel.close();
         }
-        fd.closeAll(new Closeable() {
-            public void close() throws IOException {
+
+        /*
+         * Decrement the FD use count associated with this stream
+         */
+        int useCount = fd.decrementAndGetUseCount();
+
+        /*
+         * If FileDescriptor is still in use by another stream, the finalizer
+         * will not close it.
+         */
+        if ((useCount <= 0) || !isRunningFinalize()) {
                close0();
            }
-        });
     }
 
     /**
      * Returns the <code>FileDescriptor</code>
      * object  that represents the connection to

@@ -311,13 +336,11 @@
      * @return     the file descriptor object associated with this stream.
      * @exception  IOException  if an I/O error occurs.
      * @see        java.io.FileDescriptor
      */
     public final FileDescriptor getFD() throws IOException {
-        if (fd != null) {
-            return fd;
-        }
+        if (fd != null) return fd;
         throw new IOException();
     }
 
     /**
      * Returns the unique {@link java.nio.channels.FileChannel FileChannel}

@@ -337,10 +360,17 @@
      */
     public FileChannel getChannel() {
         synchronized (this) {
             if (channel == null) {
                 channel = FileChannelImpl.open(fd, true, false, this);
+
+                /*
+                 * Increment fd's use count. Invoking the channel's close()
+                 * method will result in decrementing the use count set for
+                 * the channel.
+                 */
+                fd.incrementAndGetUseCount();
             }
             return channel;
         }
     }
 

@@ -359,14 +389,20 @@
      * @exception  IOException  if an I/O error occurs.
      * @see        java.io.FileInputStream#close()
      */
     protected void finalize() throws IOException {
         if ((fd != null) &&  (fd != FileDescriptor.in)) {
-            /* if fd is shared, the references in FileDescriptor
-             * will ensure that finalizer is only called when
-             * safe to do so. All references using the fd have
-             * become unreachable. We can call close()
+
+            /*
+             * 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);
         }
     }
+    }
 }