[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 15:09:00 UTC 2014
On 05/12/14 14:40, Peter Levart wrote:
> On 12/05/2014 03:04 PM, Chris Hegarty wrote:
>>
>> 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].
>
> What about the following:
>
>
> private boolean closed;
>
> public void close() throws IOException {
> try (OutputStream ostream = out) {
> if (!closed) flush();
> }
> closed = true;
> }
If flush throws, how is anyone supposed to know that they need to call
close again, and that it was not the close that actually threw?
close() should be a one-time shot.
I personally think that this was an oversight of the original change in
7015589, and not a deliberate behavior. My proposed change will restore
the behavior of close to pre 7015589, though not swallow the IOException
from flush.
-Chris.
> ...this way the impact of the change is minimal and still addresses the
> problem of JDK-8054565 <https://bugs.openjdk.java.net/browse/JDK-8054565>.
>
> If either flush() or out.close() throw exception the 1st time close() is
> called, they will both be called also the 2nd time. Only after flush()
> and out.close() return successfully, then further flush() invocations
> are suppressed.
>
> The alternative would be to not suppress flush() invocations, but just
> any exception thrown from flush():
>
>
> private boolean closed;
>
> public void close() throws IOException {
> try (OutputStream ostream = out) {
> try {
> flush();
> } catch (IOException | RuntimeException | Error e) {
> if (!closed) throw e;
> }
> }
> closed = true;
> }
>
>
> But I don't know if this is better or worse. It certainly could still be
> squeezed under the spec which says:
>
> * The <code>close</code> method of <code>FilterOutputStream</code>
> * calls its <code>flush</code> method, and then calls the
> * <code>close</code> method of its underlying output stream.
>
>
> Regards, Peter
>
>>
>> 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