Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -217,21 +217,37 @@ private void assureReadyAndDrainedTurnsFalse() {
*/
private void runOrBuffer(ActionItem actionItem) throws IOException {
WriteState curState = writeState.get();
if (curState.readyAndDrained) { // write to the outputStream directly
actionItem.run();
if (actionItem == completeAction) {

if (curState.readyAndDrained) {
if (isReady.getAsBoolean()) {
// Path 1: Container is truly ready. Write directly.
actionItem.run();
if (actionItem == completeAction) {
return;
}
if (!isReady.getAsBoolean()) {
boolean successful =
writeState.compareAndSet(curState, curState.withReadyAndDrained(false));
LockSupport.unpark(parkingThread);
checkState(successful, "Bug: curState is unexpectedly changed by another thread");
log.finest("the servlet output stream becomes not ready");
}
return;
}
if (!isReady.getAsBoolean()) {
boolean successful =
writeState.compareAndSet(curState, curState.withReadyAndDrained(false));
LockSupport.unpark(parkingThread);
checkState(successful, "Bug: curState is unexpectedly changed by another thread");
log.finest("the servlet output stream becomes not ready");
}
} else { // buffer to the writeChain
writeChain.offer(actionItem);
if (!writeState.compareAndSet(curState, curState.withReadyAndDrained(false))) {
}

// Path 2: Container is secretly not ready (Tomcat bug) OR already known to be false.
// We must safely buffer the item and ensure the state reflects reality.
writeChain.offer(actionItem);
if (!writeState.compareAndSet(curState, curState.withReadyAndDrained(false))) {
// CAS failed. State changed mid-flight.
if (curState.readyAndDrained) {
// Started as true, but CAS failed because another thread
// concurrently buffered and flipped it to false.
// Safe to do nothing. The winning thread handles the unpark.
} else {
// Started as false, CAS failed because onWritePossible flipped it to true.
// Original logic: retry the write since it's ready again.
checkState(
writeState.get().readyAndDrained,
"Bug: onWritePossible() should have changed readyAndDrained to true, but not");
Expand All @@ -240,7 +256,15 @@ private void runOrBuffer(ActionItem actionItem) throws IOException {
checkState(lastItem == actionItem, "Bug: lastItem != actionItem");
runOrBuffer(lastItem);
}
} // state has not changed since
}
} else {
// CAS succeeded!
// CRITICAL FIX: If we just flipped the state from true to false,
// we MUST wake up the container!
if (curState.readyAndDrained) {
LockSupport.unpark(parkingThread);
log.finest("the servlet output stream becomes not ready");
}
}
}

Expand Down
Loading