Skip to content

Commit

Permalink
Fix #6227 Async timeout dispatch race
Browse files Browse the repository at this point in the history
Only allow the thread calling onTimeout to call dispatch and complete once timeout has expired.

Signed-off-by: Greg Wilkins <gregw@webtide.com>
  • Loading branch information
gregw committed Apr 30, 2021
1 parent 2f19c67 commit 9a03f4d
Showing 1 changed file with 39 additions and 19 deletions.
Expand Up @@ -155,6 +155,7 @@ public enum Action
private boolean _asyncWritePossible;
private long _timeoutMs = DEFAULT_TIMEOUT;
private AsyncContextEvent _event;
private Thread _onTimeoutThread;

protected HttpChannelState(HttpChannel channel)
{
Expand Down Expand Up @@ -582,7 +583,10 @@ public void dispatch(ServletContext context, String path)
switch (_requestState)
{
case ASYNC:
break;
case EXPIRING:
if (Thread.currentThread() != _onTimeoutThread)
throw new IllegalStateException(this.getStatusStringLocked());
break;
default:
throw new IllegalStateException(this.getStatusStringLocked());
Expand Down Expand Up @@ -646,36 +650,47 @@ protected void onTimeout()
throw new IllegalStateException(toStringLocked());
event = _event;
listeners = _asyncListeners;
_onTimeoutThread = Thread.currentThread();
}

if (listeners != null)
try
{
Runnable task = new Runnable()
if (listeners != null)
{
@Override
public void run()
Runnable task = new Runnable()
{
for (AsyncListener listener : listeners)
@Override
public void run()
{
try
{
listener.onTimeout(event);
}
catch (Throwable x)
for (AsyncListener listener : listeners)
{
LOG.warn("{} while invoking onTimeout listener {}", x, listener, x);
try
{
listener.onTimeout(event);
}
catch (Throwable x)
{
LOG.warn("{} while invoking onTimeout listener {}", x, listener, x);
}
}
}
}

@Override
public String toString()
{
return "onTimeout";
}
};
@Override
public String toString()
{
return "onTimeout";
}
};

runInContext(event, task);
runInContext(event, task);
}
}
finally
{
synchronized (this)
{
_onTimeoutThread = null;
}
}
}

Expand All @@ -692,6 +707,11 @@ public void complete()
switch (_requestState)
{
case EXPIRING:
if (Thread.currentThread() != _onTimeoutThread)
throw new IllegalStateException(this.getStatusStringLocked());
_requestState = _sendError ? RequestState.BLOCKING : RequestState.COMPLETE;
break;

case ASYNC:
_requestState = _sendError ? RequestState.BLOCKING : RequestState.COMPLETE;
break;
Expand Down

0 comments on commit 9a03f4d

Please sign in to comment.