diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java b/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java index 9359e38f7d2..e55263c1954 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java @@ -307,9 +307,36 @@ abstract public class WriteFlusher private void fail(Callback callback, Throwable... suppressed) { - FailedState failed = (FailedState)_state.get(); + Throwable cause; + loop: + while (true) + { + State state = _state.get(); + + switch (state.getType()) + { + case FAILED: + { + FailedState failed = (FailedState)state; + cause = failed.getCause(); + break loop; + } + + case IDLE: + for (Throwable t : suppressed) + LOG.warn(t); + return; + + default: + Throwable t = new IllegalStateException(); + if (!_state.compareAndSet(state, new FailedState(t))) + continue; + + cause = t; + break loop; + } + } - Throwable cause = failed.getCause(); for (Throwable t : suppressed) { if (t != cause) diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/WriteFlusherTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/WriteFlusherTest.java index 185bc0a783e..f1d6aeb2b32 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/WriteFlusherTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/WriteFlusherTest.java @@ -18,14 +18,6 @@ package org.eclipse.jetty.io; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.instanceOf; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - import java.io.IOException; import java.io.InterruptedIOException; import java.nio.ByteBuffer; @@ -43,10 +35,18 @@ import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.FutureCallback; +import org.eclipse.jetty.util.log.StacklessLogging; import org.hamcrest.Matchers; - import org.junit.jupiter.api.Test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + public class WriteFlusherTest { @Test @@ -159,6 +159,43 @@ public class WriteFlusherTest assertTrue(flusher.isIdle()); } + @Test + public void testCallbackThrows() throws Exception + { + ByteArrayEndPoint endPoint = new ByteArrayEndPoint(new byte[0], 100); + + AtomicBoolean incompleteFlush = new AtomicBoolean(false); + WriteFlusher flusher = new WriteFlusher(endPoint) + { + @Override + protected void onIncompleteFlush() + { + incompleteFlush.set(true); + } + }; + + FutureCallback callback = new FutureCallback() + { + @Override + public void succeeded() + { + super.succeeded(); + throw new IllegalStateException(); + } + }; + + try (StacklessLogging stacklessLogging = new StacklessLogging(WriteFlusher.class)) + { + flusher.write(callback, BufferUtil.toBuffer("How now brown cow!")); + callback.get(100, TimeUnit.MILLISECONDS); + } + + assertEquals("How now brown cow!", endPoint.takeOutputString()); + assertTrue(callback.isDone()); + assertFalse(incompleteFlush.get()); + assertTrue(flusher.isIdle()); + } + @Test public void testCloseWhileBlocking() throws Exception {