406390 406617 removed tiny race from handling of suspend and complete

This commit is contained in:
Greg Wilkins 2013-05-01 09:14:05 +10:00
parent 9ad5ab1ed5
commit 7590ad6795
3 changed files with 63 additions and 31 deletions

View File

@ -43,6 +43,7 @@ import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.ChannelEndPoint;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.server.HttpChannelState.Next;
import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
@ -216,6 +217,15 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
@Override
public void run()
{
handle();
}
/* ------------------------------------------------------------ */
/**
* @return True if the channel is ready to continue handling (ie it is not suspended)
*/
public boolean handle()
{
LOG.debug("{} handle enter", this);
@ -228,15 +238,16 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
Thread.currentThread().setName(threadName + " - " + _uri);
}
// Loop here to handle async request redispatches.
// The loop is controlled by the call to async.unhandle in the
// finally block below. Unhandle will return false only if an async dispatch has
// already happened when unhandle is called.
HttpChannelState.Next next = _state.handling();
try
{
// Loop here to handle async request redispatches.
// The loop is controlled by the call to async.unhandle in the
// finally block below. Unhandle will return false only if an async dispatch has
// already happened when unhandle is called.
boolean handling = _state.handling();
while (handling && getServer().isRunning())
while (next==Next.CONTINUE && getServer().isRunning())
{
try
{
@ -286,9 +297,11 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
}
finally
{
handling = !_state.unhandle();
next = _state.unhandle();
}
}
if (next==Next.WAIT)
return false;
}
finally
{
@ -296,7 +309,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
Thread.currentThread().setName(threadName);
setCurrentHttpChannel(null);
if (_state.isCompleting())
if (next==Next.COMPLETE)
{
try
{
@ -319,13 +332,21 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
}
finally
{
_request.setHandled(true);
_transport.completed();
next=Next.RECYCLE;
}
}
if (next==Next.RECYCLE)
{
_request.setHandled(true);
_transport.completed();
}
LOG.debug("{} handle exit", this);
}
return true;
}
/**
@ -570,10 +591,9 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
try
{
if (_state.handling())
if (_state.handling()==Next.CONTINUE)
{
commitResponse(new ResponseInfo(HttpVersion.HTTP_1_1,new HttpFields(),0,status,reason,false),null,true);
_state.unhandle();
}
}
catch (IOException e)
@ -581,8 +601,9 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
LOG.warn(e);
}
finally
{
_state.completed();
{
if (_state.unhandle()==Next.COMPLETE)
_state.completed();
}
}

View File

@ -71,6 +71,14 @@ public class HttpChannelState
COMPLETING, // Request is completable
COMPLETED // Request is complete
}
public enum Next
{
CONTINUE, // Continue handling the channel
WAIT, // Wait for further events
COMPLETE, // Complete the channel
RECYCLE, // Channel is completed
}
private final HttpChannel<?> _channel;
private List<AsyncListener> _lastAsyncListeners;
@ -154,9 +162,9 @@ public class HttpChannelState
}
/**
* @return true if the handling of the request should proceed
* @return Next handling of the request should proceed
*/
protected boolean handling()
protected Next handling()
{
synchronized (this)
{
@ -178,12 +186,16 @@ public class HttpChannelState
case COMPLETECALLED:
_state=State.COMPLETING;
return false;
return Next.COMPLETE;
case ASYNCWAIT:
case COMPLETING:
return Next.COMPLETE;
case ASYNCWAIT:
return Next.WAIT;
case COMPLETED:
return false;
return Next.RECYCLE;
case REDISPATCH:
_state=State.REDISPATCHED;
@ -194,7 +206,7 @@ public class HttpChannelState
}
_responseWrapped=false;
return true;
return Next.CONTINUE;
}
}
@ -255,10 +267,10 @@ public class HttpChannelState
* Signal that the HttpConnection has finished handling the request.
* For blocking connectors, this call may block if the request has
* been suspended (startAsync called).
* @return true if handling is complete, false if the request should
* @return next actions
* be handled again (eg because of a resume that happened before unhandle was called)
*/
protected boolean unhandle()
protected Next unhandle()
{
synchronized (this)
{
@ -267,7 +279,7 @@ public class HttpChannelState
case REDISPATCHED:
case DISPATCHED:
_state=State.COMPLETING;
return true;
return Next.COMPLETE;
case IDLE:
throw new IllegalStateException(this.getStatusString());
@ -277,25 +289,25 @@ public class HttpChannelState
_state=State.ASYNCWAIT;
scheduleTimeout();
if (_state==State.ASYNCWAIT)
return true;
return Next.WAIT;
else if (_state==State.COMPLETECALLED)
{
_state=State.COMPLETING;
return true;
return Next.COMPLETE;
}
_initial=false;
_state=State.REDISPATCHED;
return false;
return Next.CONTINUE;
case REDISPATCHING:
_initial=false;
_state=State.REDISPATCHED;
return false;
return Next.CONTINUE;
case COMPLETECALLED:
_initial=false;
_state=State.COMPLETING;
return true;
return Next.COMPLETE;
default:
throw new IllegalStateException(this.getStatusString());

View File

@ -222,11 +222,10 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
// The parser returned true, which indicates the channel is ready to handle a request.
// Call the channel and this will either handle the request/response to completion OR,
// if the request suspends, the request/response will be incomplete so the outer loop will exit.
_channel.run();
boolean handle=_channel.handle();
// Return if suspended or upgraded
if (_channel.getState().isSuspended() || getEndPoint().getConnection()!=this)
if (!handle || getEndPoint().getConnection()!=this)
return;
}
else if (BufferUtil.isEmpty(_requestBuffer))