/*
* Copyright 2011 LMAX Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.lmax.disruptor;
/**
* Convenience class for handling the batching semantics of consuming entries from a {@link RingBuffer}
* and delegating the available {@link AbstractEntry}s to a {@link BatchHandler}.
*
* If the {@link BatchHandler} also implements {@link LifecycleAware} it will be notified just after the thread
* is started and just before the thread is shutdown.
*
* @param <T> Entry implementation storing the data for sharing during exchange or parallel coordination of an event.
*/
public final class BatchConsumer<T extends AbstractEntry>
implements Consumer
{
private final ConsumerBarrier<T> consumerBarrier;
private final BatchHandler<T> handler;
private ExceptionHandler exceptionHandler = new FatalExceptionHandler();
private volatile boolean running = true;
public long p1, p2, p3, p4, p5, p6, p7; // cache line padding
private volatile long sequence = RingBuffer.INITIAL_CURSOR_VALUE;
public long p8, p9, p10, p11, p12, p13, p14; // cache line padding
/**
* Construct a batch consumer that will automatically track the progress by updating its sequence when
* the {@link BatchHandler#onAvailable(AbstractEntry)} method returns.
*
* @param consumerBarrier on which it is waiting.
* @param handler is the delegate to which {@link AbstractEntry}s are dispatched.
*/
public BatchConsumer(final ConsumerBarrier<T> consumerBarrier,
final BatchHandler<T> handler)
{
this.consumerBarrier = consumerBarrier;
this.handler = handler;
}
/**
* Construct a batch consumer that will rely on the {@link SequenceTrackingHandler}
* to callback via the {@link BatchConsumer.SequenceTrackerCallback} when it has
* completed with a sequence within a batch. Sequence will be updated at the end of
* a batch regardless.
*
* @param consumerBarrier on which it is waiting.
* @param entryHandler is the delegate to which {@link AbstractEntry}s are dispatched.
*/
public BatchConsumer(final ConsumerBarrier<T> consumerBarrier,
final SequenceTrackingHandler<T> entryHandler)
{
this.consumerBarrier = consumerBarrier;
this.handler = entryHandler;
entryHandler.setSequenceTrackerCallback(new SequenceTrackerCallback());
}
@Override
public long getSequence()
{
return sequence;
}
@Override
public void halt()
{
running = false;
consumerBarrier.alert();
}
/**
* Set a new {@link ExceptionHandler} for handling exceptions propagated out of the {@link BatchConsumer}
*
* @param exceptionHandler to replace the existing exceptionHandler.
*/
public void setExceptionHandler(final ExceptionHandler exceptionHandler)
{
if (null == exceptionHandler)
{
throw new NullPointerException();
}
this.exceptionHandler = exceptionHandler;
}
/**
* Get the {@link ConsumerBarrier} the {@link Consumer} is waiting on.
*
* @return the barrier this {@link Consumer} is using.
*/
public ConsumerBarrier<? extends T> getConsumerBarrier()
{
return consumerBarrier;
}
/**
* It is ok to have another thread rerun this method after a halt().
*/
@Override
public void run()
{
running = true;
if (LifecycleAware.class.isAssignableFrom(handler.getClass()))
{
((LifecycleAware)handler).onStart();
}
T entry = null;
long nextSequence = sequence + 1 ;
while (running)
{
try
{
final long availableSequence = consumerBarrier.waitFor(nextSequence);
while (nextSequence <= availableSequence)
{
entry = consumerBarrier.getEntry(nextSequence);
handler.onAvailable(entry);
nextSequence++;
}
handler.onEndOfBatch();
sequence = entry.getSequence();
}
catch (final AlertException ex)
{
// Wake up from blocking wait and check if we should continue to run
}
catch (final Exception ex)
{
exceptionHandler.handle(ex, entry);
sequence = entry.getSequence();
nextSequence = entry.getSequence() + 1;
}
}
if (LifecycleAware.class.isAssignableFrom(handler.getClass()))
{
((LifecycleAware)handler).onShutdown();
}
}
/**
* Used by the {@link BatchHandler} to signal when it has completed consuming a given sequence.
*/
public final class SequenceTrackerCallback
{
/**
* Notify that the handler has consumed up to a given sequence.
*
* @param sequence that has been consumed.
*/
public void onCompleted(final long sequence)
{
BatchConsumer.this.sequence = sequence;
}
}
}