424043 - IteratingCallback Idle race.

A few renamings and fixes to avoid IllegalStateExceptions.
This commit is contained in:
Simone Bordet 2013-12-15 23:50:37 +01:00
parent 8bf4a4f263
commit 8d621a9331
8 changed files with 55 additions and 57 deletions

View File

@ -694,14 +694,14 @@ public abstract class HttpSender implements AsyncContentProvider.Listener
{
// There is more content to send
sendContent(exchange, content, this);
return Action.EXECUTING;
return Action.SCHEDULED;
}
if (content.isConsumed())
{
sendContent(exchange, content, lastCallback);
return Action.EXECUTING;
return Action.SCHEDULED;
}
while (true)
@ -725,7 +725,7 @@ public abstract class HttpSender implements AsyncContentProvider.Listener
LOG.debug("Deferred content available for {}", request);
// TODO: this case is not covered by tests
sendContent(exchange, content, this);
return Action.EXECUTING;
return Action.SCHEDULED;
}
break;
}

View File

@ -567,7 +567,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
}
else
continue;
return Action.EXECUTING;
return Action.SCHEDULED;
}
case SHUTDOWN_OUT:
{
@ -684,7 +684,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
}
else
continue;
return Action.EXECUTING;
return Action.SCHEDULED;
}
case SHUTDOWN_OUT:
{

View File

@ -763,14 +763,14 @@ public class HttpOutput extends ServletOutputStream implements Runnable
{
_flushed=true;
write(_aggregate, false, this);
return Action.EXECUTING;
return Action.SCHEDULED;
}
if (!_flushed)
{
_flushed=true;
write(BufferUtil.EMPTY_BUFFER,false,this);
return Action.EXECUTING;
return Action.SCHEDULED;
}
return Action.SUCCEEDED;
@ -813,7 +813,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
{
_completed=_len==0;
write(_aggregate, _complete && _completed, this);
return Action.EXECUTING;
return Action.SCHEDULED;
}
// Can we just aggregate the remainder?
@ -831,7 +831,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
{
_completed=true;
write(_buffer, _complete, this);
return Action.EXECUTING;
return Action.SCHEDULED;
}
// otherwise take a slice
@ -843,7 +843,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
_slice.position(p);
_completed=!_buffer.hasRemaining();
write(_slice, _complete && _completed, this);
return Action.EXECUTING;
return Action.SCHEDULED;
}
// all content written, but if we have not yet signal completion, we
@ -854,7 +854,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
{
_completed=true;
write(BufferUtil.EMPTY_BUFFER, _complete, this);
return Action.EXECUTING;
return Action.SCHEDULED;
}
closed();
}
@ -914,7 +914,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
_buffer.position(0);
_buffer.limit(len);
write(_buffer,_eof,this);
return Action.EXECUTING;
return Action.SCHEDULED;
}
@Override
@ -978,7 +978,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
_buffer.flip();
write(_buffer,_eof,this);
return Action.EXECUTING;
return Action.SCHEDULED;
}
@Override

View File

@ -324,7 +324,7 @@ public class GzipHttpOutput extends HttpOutput
addTrailer();
superWrite(_buffer,complete,this);
return Action.EXECUTING;
return Action.SCHEDULED;
}
}
@ -389,7 +389,7 @@ public class GzipHttpOutput extends HttpOutput
addTrailer();
superWrite(_buffer,complete,this);
return Action.EXECUTING;
return Action.SCHEDULED;
}
}

View File

@ -213,13 +213,13 @@ public class Flusher
// MAX_GATHER parameter, and/or autotune the buffer returned
// by FrameBytes.getByteBuffer() (see also comment there).
return Action.EXECUTING;
return Action.SCHEDULED;
}
@Override
protected void completed()
{
// will never be called as process always returns EXECUTING or IDLE
// will never be called as process always returns SCHEDULED or IDLE
throw new IllegalStateException();
}

View File

@ -67,7 +67,7 @@ public abstract class IteratingCallback implements Callback
* a sub task, where the execution has started but the callback
* may have not yet been invoked.
*/
EXECUTING,
SCHEDULED,
/**
* Indicates that {@link #process()} has completed the overall job.
*/
@ -88,7 +88,7 @@ public abstract class IteratingCallback implements Callback
* <ul>
* <li>{@link Action#IDLE} when no sub tasks are available for execution
* but the overall job is not completed yet</li>
* <li>{@link Action#EXECUTING} when the sub task asynchronous execution
* <li>{@link Action#SCHEDULED} when the sub task asynchronous execution
* has been started</li>
* <li>{@link Action#SUCCEEDED} when the overall job is completed</li>
* <li>{@link Action#FAILED} when the overall job cannot be completed</li>
@ -123,7 +123,7 @@ public abstract class IteratingCallback implements Callback
{
case INACTIVE:
{
if (perform())
if (processIterations())
return;
break;
}
@ -146,7 +146,7 @@ public abstract class IteratingCallback implements Callback
}
}
private boolean perform() throws Exception
private boolean processIterations() throws Exception
{
// Keeps iterating as long as succeeded() is called during process().
// If we are in INACTIVE state, either this is the first iteration or
@ -157,7 +157,7 @@ public abstract class IteratingCallback implements Callback
// it is guarded by the CaS above. However, the case blocks below may
// be executed concurrently in this case: T1 calls process() which
// executes the asynchronous sub task, which calls succeeded(), which
// moves the state into INACTIVE, then returns EXECUTING; T2 calls
// moves the state into INACTIVE, then returns SCHEDULED; T2 calls
// iterate(), state is now INACTIVE and process() is called again and
// returns another action. Now we have 2 threads that may execute the
// action case blocks below concurrently; therefore each case block
@ -181,13 +181,13 @@ public abstract class IteratingCallback implements Callback
// State may have changed concurrently, try again.
continue;
}
case EXECUTING:
case SCHEDULED:
{
// The sub task is executing, and the callback for it may or
// may not have already been called yet, which we figure out below.
// Can double CaS here because state never changes directly ITERATING_AGAIN --> ITERATE.
if (_state.compareAndSet(State.ITERATING, State.SCHEDULED) ||
_state.compareAndSet(State.ITERATE_AGAIN, State.SCHEDULED))
if (_state.compareAndSet(State.ITERATING, State.ACTIVE) ||
_state.compareAndSet(State.ITERATE_AGAIN, State.ACTIVE))
// Not called back yet, so wait.
return true;
// Call back must have happened, so iterate.
@ -196,13 +196,13 @@ public abstract class IteratingCallback implements Callback
case SUCCEEDED:
{
// The overall job has completed.
success();
completed();
if (completeSuccess())
completed();
return true;
}
case FAILED:
{
failure();
completeFailure();
return true;
}
default:
@ -234,18 +234,14 @@ public abstract class IteratingCallback implements Callback
return;
continue;
}
case SCHEDULED:
case ACTIVE:
{
// If we can move from SCHEDULED to INACTIVE
// then we continue in order to hit the INACTIVE
// case, since we need to iterate() more.
// If we can move from ACTIVE to INACTIVE
// then we are responsible to call iterate().
if (_state.compareAndSet(current, State.INACTIVE))
continue;
continue;
}
case INACTIVE:
{
iterate();
iterate();
// If we can't CaS, then failed() must have been
// called, and we just return.
return;
}
default:
@ -264,34 +260,36 @@ public abstract class IteratingCallback implements Callback
@Override
public void failed(Throwable x)
{
failure();
completeFailure();
}
private void success()
private boolean completeSuccess()
{
while (true)
{
State current = _state.get();
if (current == State.FAILED)
{
throw new IllegalStateException(toString());
// Success arrived too late, sorry.
return false;
}
else
{
if (_state.compareAndSet(current, State.SUCCEEDED))
break;
return true;
}
}
}
private void failure()
private void completeFailure()
{
while (true)
{
State current = _state.get();
if (current == State.SUCCEEDED)
{
throw new IllegalStateException(toString());
// Failed arrived too late, sorry.
return;
}
else
{
@ -342,10 +340,10 @@ public abstract class IteratingCallback implements Callback
INACTIVE,
/**
* This callback is iterating and {@link #process()} has scheduled an
* asynchronous operation by returning {@link Action#EXECUTING}, but
* asynchronous operation by returning {@link Action#SCHEDULED}, but
* the operation is still undergoing.
*/
SCHEDULED,
ACTIVE,
/**
* This callback is iterating and {@link #process()} has been called
* but not returned yet.

View File

@ -60,7 +60,7 @@ public class IteratingCallbackTest
if (i-->1)
{
succeeded(); // fake a completed IO operation
return Action.EXECUTING;
return Action.SCHEDULED;
}
return Action.SUCCEEDED;
}
@ -87,7 +87,7 @@ public class IteratingCallbackTest
if (i-->1)
{
scheduler.schedule(successTask,50,TimeUnit.MILLISECONDS);
return Action.EXECUTING;
return Action.SCHEDULED;
}
return Action.SUCCEEDED;
}
@ -114,7 +114,7 @@ public class IteratingCallbackTest
if (i-->1)
{
scheduler.schedule(successTask,50,TimeUnit.MILLISECONDS);
return Action.EXECUTING;
return Action.SCHEDULED;
}
return Action.SUCCEEDED;
}
@ -154,7 +154,7 @@ public class IteratingCallbackTest
succeeded(); // fake a completed IO operation
else
failed(new Exception("testing"));
return Action.EXECUTING;
return Action.SCHEDULED;
}
return Action.SUCCEEDED;
}
@ -179,7 +179,7 @@ public class IteratingCallbackTest
if (i-->1)
{
scheduler.schedule(i>2?successTask:failTask,50,TimeUnit.MILLISECONDS);
return Action.EXECUTING;
return Action.SCHEDULED;
}
return Action.SUCCEEDED;
}
@ -210,11 +210,11 @@ public class IteratingCallbackTest
{
case 5:
succeeded();
return Action.EXECUTING;
return Action.SCHEDULED;
case 4:
scheduler.schedule(successTask,5,TimeUnit.MILLISECONDS);
return Action.EXECUTING;
return Action.SCHEDULED;
case 3:
scheduler.schedule(new Runnable()
@ -229,11 +229,11 @@ public class IteratingCallbackTest
case 2:
succeeded();
return Action.EXECUTING;
return Action.SCHEDULED;
case 1:
scheduler.schedule(successTask,5,TimeUnit.MILLISECONDS);
return Action.EXECUTING;
return Action.SCHEDULED;
case 0:
return Action.SUCCEEDED;

View File

@ -212,7 +212,7 @@ public class FrameFlusher
@Override
protected void completed()
{
// will never be called as process always returns EXECUTING or IDLE
// will never be called as process always returns SCHEDULED or IDLE
throw new IllegalStateException();
}
@ -245,7 +245,7 @@ public class FrameFlusher
endpoint.write(this,buffers.toArray(new ByteBuffer[buffers.size()]));
buffers.clear();
return Action.EXECUTING;
return Action.SCHEDULED;
}
@Override