src/share/classes/java/io/FileOutputStream.java
Print this page
@@ -67,11 +67,20 @@
*/
private FileChannel channel;
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 file output stream to write to the file with the
* specified name. A new <code>FileDescriptor</code> object is
* created to represent this file connection.
* <p>
@@ -197,11 +206,11 @@
throw new NullPointerException();
}
this.fd = new FileDescriptor();
this.append = append;
- fd.attach(this);
+ fd.incrementAndGetUseCount();
open(name, append);
}
/**
* Creates a file output stream to write to the specified file
@@ -234,11 +243,17 @@
if (security != null) {
security.checkWrite(fdObj);
}
this.fd = fdObj;
this.append = false;
- fd.attach(this);
+
+ /*
+ * FileDescriptor is being shared by streams.
+ * Ensure that it's GC'ed only when all the streams/channels are done
+ * using it.
+ */
+ fd.incrementAndGetUseCount();
}
/**
* Opens a file, with the specified name, for overwriting or appending.
* @param name name of file to be opened
@@ -323,17 +338,31 @@
}
closed = true;
}
if (channel != null) {
+ /*
+ * Decrement 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 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 file descriptor associated with this stream.
*
@@ -343,13 +372,11 @@
*
* @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}
@@ -370,10 +397,17 @@
*/
public FileChannel getChannel() {
synchronized (this) {
if (channel == null) {
channel = FileChannelImpl.open(fd, false, true, append, 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;
}
}
@@ -388,18 +422,24 @@
protected void finalize() throws IOException {
if (fd != null) {
if (fd == FileDescriptor.out || fd == FileDescriptor.err) {
flush();
} else {
- /* 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);
}
}
+ }
}
private native void close0() throws IOException;
private static native void initIDs();