-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixes #11016 - Jetty 12 IllegalStateException when stopping Server wi… #11017
Changes from 6 commits
dae910a
6c839f6
efee6f0
d93cba7
ac47524
586d57a
6377c57
ad46276
80c183c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -151,6 +151,7 @@ public enum Action | |
private long _timeoutMs = DEFAULT_TIMEOUT; | ||
private AsyncContextEvent _event; | ||
private Thread _onTimeoutThread; | ||
private Throwable _failure; | ||
|
||
protected ServletChannelState(ServletChannel servletChannel) | ||
{ | ||
|
@@ -292,19 +293,13 @@ public String getStatusString() | |
} | ||
} | ||
|
||
public boolean completeResponse() | ||
public Throwable completeResponse() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The boolean return was giving protection against calling this method twice. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method is only called once because it is protected by the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So can we have an assert (or ISE) if it is already in COMPLETED state There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure what you refer to, as there are many COMPLETED states? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method used to return a boolean that indicated if it had just transitioned from OPEN->COMPLETED. If it returned false, then the callback would not be completed. Now it returns a throwable to say if there has been a failure, in which case the callback is failed. But it has lost the protection for multiple calls, so the callback might be called twice. I understand that you know this cannot happen because of external factors.... that nothing in this method represents that invariant. It would be clearer if it had something like: assert _failure != null || _outputState == OutputState.OPEN If this assert does not hold, then this PR has changed behaviour in a non obvious way. |
||
{ | ||
try (AutoLock ignored = lock()) | ||
{ | ||
switch (_outputState) | ||
{ | ||
case OPEN: | ||
_outputState = OutputState.COMPLETED; | ||
return true; | ||
|
||
default: | ||
return false; | ||
} | ||
if (_outputState == OutputState.OPEN) | ||
_outputState = OutputState.COMPLETED; | ||
return _failure; | ||
} | ||
} | ||
|
||
|
@@ -321,7 +316,7 @@ public boolean isResponseCompleted() | |
} | ||
} | ||
|
||
public boolean abortResponse() | ||
private boolean abortResponse(Throwable failure) | ||
{ | ||
try (AutoLock ignored = lock()) | ||
{ | ||
|
@@ -331,18 +326,34 @@ public boolean abortResponse() | |
case ABORTED: | ||
return false; | ||
|
||
case OPEN: | ||
_servletChannel.getServletContextResponse().setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500); | ||
_outputState = OutputState.ABORTED; | ||
return true; | ||
|
||
default: | ||
_outputState = OutputState.ABORTED; | ||
_failure = failure; | ||
gregw marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return true; | ||
} | ||
} | ||
} | ||
|
||
public void abort(Throwable failure) | ||
{ | ||
boolean handle = false; | ||
try (AutoLock ignored = lock()) | ||
{ | ||
boolean aborted = abortResponse(failure); | ||
if (LOG.isDebugEnabled()) | ||
LOG.debug("abort={} {}", aborted, this, failure); | ||
if (aborted) | ||
{ | ||
handle = _state == State.WAITING; | ||
if (handle) | ||
_state = State.WOKEN; | ||
_requestState = RequestState.COMPLETED; | ||
} | ||
} | ||
if (handle) | ||
scheduleDispatch(); | ||
} | ||
|
||
/** | ||
* @return Next handling of the request should proceed | ||
*/ | ||
|
@@ -549,7 +560,12 @@ public String toString() | |
} | ||
} | ||
|
||
public void errorHandling() | ||
/** | ||
* Called when an asynchronous call to {@code ErrorHandler.handle()} is about to happen. | ||
* | ||
* @see #errorHandlingComplete(Throwable) | ||
*/ | ||
void errorHandling() | ||
{ | ||
try (AutoLock ignored = lock()) | ||
{ | ||
|
@@ -559,17 +575,29 @@ public void errorHandling() | |
} | ||
} | ||
|
||
public void errorHandlingComplete() | ||
/** | ||
* Called when the {@code Callback} passed to {@code ErrorHandler.handle()} is completed. | ||
* | ||
* @param failure the failure reported by the error handling, | ||
* or {@code null} if there was no failure | ||
*/ | ||
void errorHandlingComplete(Throwable failure) | ||
{ | ||
boolean handle; | ||
try (AutoLock ignored = lock()) | ||
{ | ||
if (LOG.isDebugEnabled()) | ||
LOG.debug("errorHandlingComplete {}", toStringLocked()); | ||
LOG.debug("errorHandlingComplete {}", toStringLocked(), failure); | ||
|
||
handle = _state == State.WAITING; | ||
if (handle) | ||
_state = State.WOKEN; | ||
|
||
// If there is a failure while trying to | ||
// handle a previous failure, just bail out. | ||
if (failure != null) | ||
abortResponse(failure); | ||
|
||
if (_requestState == RequestState.ERRORING) | ||
_requestState = RequestState.COMPLETE; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we say: