diff options
author | 2016-11-21 12:37:46 +0000 | |
---|---|---|
committer | 2016-11-21 19:40:54 +0000 | |
commit | 480a2cf4d3684ad9171077a279f1743192096d7b (patch) | |
tree | 46c080bdc320ecf6166a800e9602a8c9f1b32bde /src | |
parent | af25a98a7af734926f6d29187818ce4140b4f3e8 (diff) |
Make LineBufferedOutputStream resistant to exceptions thrown while flushing its buffer.
--
MOS_MIGRATED_REVID=139771073
Diffstat (limited to 'src')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/runtime/LineBufferedOutputStream.java | 20 | ||||
-rw-r--r-- | src/test/java/com/google/devtools/build/lib/runtime/LineBufferedOutputStreamTest.java | 21 |
2 files changed, 31 insertions, 10 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/LineBufferedOutputStream.java b/src/main/java/com/google/devtools/build/lib/runtime/LineBufferedOutputStream.java index 3d0c44f93c..b4f3b8583e 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/LineBufferedOutputStream.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/LineBufferedOutputStream.java @@ -36,13 +36,19 @@ public class LineBufferedOutputStream extends OutputStream { this.pos = 0; } + private void flushBuffer() throws IOException { + int oldPos = pos; + // Set pos to zero first so that if the write below throws, we are still in a consistent state. + pos = 0; + wrapped.write(buffer, 0, oldPos); + } + @Override public synchronized void write(byte[] b, int off, int inlen) throws IOException { if (inlen > buffer.length * 2) { // Do not buffer large writes if (pos > 0) { - wrapped.write(buffer, 0, pos); - pos = 0; + flushBuffer(); } wrapped.write(b, off, inlen); return; @@ -51,14 +57,8 @@ public class LineBufferedOutputStream extends OutputStream { int next = off; while (next < off + inlen) { buffer[pos++] = b[next]; - if (b[next] == '\n') { - wrapped.write(buffer, 0, pos); - pos = 0; - } - - if (pos == buffer.length) { - wrapped.write(buffer, 0, pos); - pos = 0; + if (b[next] == '\n' || pos == buffer.length) { + flushBuffer(); } next++; diff --git a/src/test/java/com/google/devtools/build/lib/runtime/LineBufferedOutputStreamTest.java b/src/test/java/com/google/devtools/build/lib/runtime/LineBufferedOutputStreamTest.java index bc136b41fb..8cf7c155d7 100644 --- a/src/test/java/com/google/devtools/build/lib/runtime/LineBufferedOutputStreamTest.java +++ b/src/test/java/com/google/devtools/build/lib/runtime/LineBufferedOutputStreamTest.java @@ -14,6 +14,7 @@ package com.google.devtools.build.lib.runtime; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; import com.google.common.base.Strings; import com.google.devtools.build.lib.testutil.Suite; @@ -35,6 +36,7 @@ import org.junit.runners.JUnit4; public class LineBufferedOutputStreamTest { private static class MockOutputStream extends OutputStream { private final List<String> writes = new ArrayList<>(); + private boolean throwException = false; @Override public void write(int byteAsInt) throws IOException { @@ -45,6 +47,10 @@ public class LineBufferedOutputStreamTest { @Override public synchronized void write(byte[] b, int off, int inlen) throws IOException { writes.add(new String(b, off, inlen, StandardCharsets.UTF_8)); + if (throwException) { + throwException = false; + throw new IOException("thrown"); + } } } @@ -73,6 +79,21 @@ public class LineBufferedOutputStreamTest { assertThat(lineBuffer("a", "a", large, large, "a")).containsExactly( "aa", large, large, "a"); + } + @Test + public void testIOErrorOnWrappedStream() throws Exception { + MockOutputStream mos = new MockOutputStream(); + LineBufferedOutputStream cut = new LineBufferedOutputStream(mos, 4); + mos.throwException = true; + try { + cut.write("aaaa".getBytes(StandardCharsets.UTF_8)); + fail("IOException expected"); + } catch (IOException e) { + // Expected. + } + cut.write("a".getBytes(StandardCharsets.UTF_8)); + cut.close(); + assertThat(mos.writes).containsExactly("aaaa", "a"); } } |