[PATCH] JDK-8054565: FilterOutputStream.close may throw IOException if called twice and underlying flush or close fails

Chris Hegarty chris.hegarty at oracle.com
Fri Dec 5 14:04:28 UTC 2014


On 05/12/14 11:38, Pavel Rappo wrote:
> +1, I couldn’t say better

Right. This bug should only try to address the change in behavior that 
was inadvertently introduced by 7015589[1][2].

I don't see any reason to make 'closed' protected ( part of the public 
Java SE API ), something like:

diff --git a/src/java.base/share/classes/java/io/FilterOutputStream.java 
b/src/java.base/share/classes/java/io/FilterOutputStream.java
--- a/src/java.base/share/classes/java/io/FilterOutputStream.java
+++ b/src/java.base/share/classes/java/io/FilterOutputStream.java
@@ -48,6 +48,8 @@
       */
      protected OutputStream out;

+    private boolean closed; // false
+
      /**
       * Creates an output stream filter built on top of the specified
       * underlying output stream.
@@ -154,6 +156,9 @@
       */
      @SuppressWarnings("try")
      public void close() throws IOException {
+        if (closed)
+            return;
+        closed = true;
          try (OutputStream ostream = out) {
              flush();
          }

diff --git a/test/java/io/etc/FailingFlushAndClose.java 
b/test/java/io/etc/FailingFlushAndClose.java
--- a/test/java/io/etc/FailingFlushAndClose.java
+++ b/test/java/io/etc/FailingFlushAndClose.java
@@ -25,7 +25,7 @@

  /**
   * @test
- * @bug 7015589
+ * @bug 7015589 8054565
   * @summary Test that buffering streams are considered closed even 
when the
   *    close or flush from the underlying stream fails.
   */
@@ -165,7 +165,7 @@
          }
      }

-    static void testFailingClose(InputStream in) throws IOException {
+    static InputStream testFailingClose(InputStream in) throws 
IOException {
          System.out.println(in.getClass());
          in.read(new byte[100]);
          try {
@@ -176,9 +176,10 @@
              in.read(new byte[100]);
              fail("read did not fail");
          } catch (IOException expected) { }
+        return in;
      }

-    static void testFailingClose(OutputStream out) throws IOException {
+    static OutputStream testFailingClose(OutputStream out) throws 
IOException {
          System.out.println(out.getClass());
          out.write(1);
          try {
@@ -190,9 +191,10 @@
              if (!(out instanceof BufferedOutputStream))
                  fail("write did not fail");
          } catch (IOException expected) { }
+        return out;
      }

-    static void testFailingFlush(OutputStream out) throws IOException {
+    static OutputStream testFailingFlush(OutputStream out) throws 
IOException {
          System.out.println(out.getClass());
          out.write(1);
          try {
@@ -206,9 +208,27 @@
                  fail("close did not fail");
              } catch (IOException expected) { }
          }
+        return out;
      }

-    static void testFailingClose(Reader r) throws IOException {
+    static void closeAgain(InputStream in) throws IOException {
+        // assert the given stream should already be closed.
+        try {
+            in.close();
+        } catch (IOException expected) {
+            fail("unexpected IOException from subsequent close");
+        }
+    }
+    static void closeAgain(OutputStream out) throws IOException {
+        // assert the given stream should already be closed.
+        try {
+            out.close();
+        } catch (IOException expected) {
+            fail("unexpected IOException from subsequent close");
+        }
+    }
+
+    static Reader testFailingClose(Reader r) throws IOException {
          System.out.println(r.getClass());
          r.read(new char[100]);
          try {
@@ -219,9 +239,10 @@
              r.read(new char[100]);
              fail("read did not fail");
          } catch (IOException expected) { }
+        return r;
      }

-    static void testFailingClose(Writer w) throws IOException {
+    static Writer testFailingClose(Writer w) throws IOException {
          System.out.println(w.getClass());
          w.write("message");
          try {
@@ -232,9 +253,10 @@
              w.write("another message");
              fail("write did not fail");
          } catch (IOException expected) { }
+        return w;
      }

-    static void testFailingFlush(Writer w) throws IOException {
+    static Writer testFailingFlush(Writer w) throws IOException {
          System.out.println(w.getClass());
          w.write("message");
          try {
@@ -249,18 +271,38 @@
                  fail("close did not fail");
              } catch (IOException expected) { }
          }
+        return w;
+    }
+
+    static Reader closeAgain(Reader r) throws IOException {
+        // assert the given stream should already be closed.
+        try {
+            r.close();
+        } catch (IOException expected) {
+            fail("unexpected IOException from subsequent close");
+        }
+        return r;
+    }
+    static Writer closeAgain(Writer w) throws IOException {
+        // assert the given stream should already be closed.
+        try {
+            w.close();
+        } catch (IOException expected) {
+            fail("unexpected IOException from subsequent close");
+        }
+        return w;
      }

      public static void main(String[] args) throws IOException {

-        testFailingClose(new BufferedInputStream(new 
FailingCloseInputStream()));
-        testFailingClose(new BufferedOutputStream(new 
FailingCloseOutputStream()));
+        closeAgain(testFailingClose(new BufferedInputStream(new 
FailingCloseInputStream())));
+        closeAgain(testFailingClose(new BufferedOutputStream(new 
FailingCloseOutputStream())));

-        testFailingClose(new BufferedReader(new FailingCloseReader()));
-        testFailingClose(new BufferedWriter(new FailingCloseWriter()));
+        closeAgain(testFailingClose(new BufferedReader(new 
FailingCloseReader())));
+        closeAgain(testFailingClose(new BufferedWriter(new 
FailingCloseWriter())));

-        testFailingFlush(new BufferedOutputStream(new 
FailingFlushOutputStream()));
-        testFailingFlush(new BufferedWriter(new FailingFlushWriter()));
+        closeAgain(testFailingFlush(new BufferedOutputStream(new 
FailingFlushOutputStream())));
+        closeAgain(testFailingFlush(new BufferedWriter(new 
FailingFlushWriter())));

          if (failed > 0)
              throw new RuntimeException(failed + " test(s) failed - see 
log for details");


-Chris.

[1] https://bugs.openjdk.java.net/browse/JDK-7015589
[2] http://hg.openjdk.java.net/jdk8/jdk8/jdk/rev/759aa847dcaf


> -Pavel
>
>> On 5 Dec 2014, at 01:18, Vitaly Davidovich <vitalyd at gmail.com> wrote:
>>
>> Attempting to make close() atomic just makes the next reader
>> confused when the rest of the class isn't and may also penalize single
>> threaded callers of close().
>



More information about the core-libs-dev mailing list