package rocks.inspectit.server.cache.impl;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import rocks.inspectit.server.cache.IBufferElement;
import rocks.inspectit.shared.all.communication.DefaultData;
/**
* Abstract class for indexing and analyzing processing.
*
* @param <E>
* Type of data to process.
*
* @author Ivan Senic
*
*/
abstract class AbstractBufferElementProcessor<E extends DefaultData> {
/**
* {@link AtomicBuffer} to work on.
*/
protected final AtomicBuffer<E> atomicBuffer;
/**
* Reference to the last processed element.
*/
protected AtomicReference<IBufferElement<E>> lastProcessed;
/**
* Lock to use during operation.
*/
private Lock lock;
/**
* Condition to wait on when there s nothing to process.
*/
private Condition condition;
/**
* Default constructor.
*
* @param atomicBuffer
* {@link AtomicBuffer} to work on.
* @param lastProcessed
* Reference to the last processed element.
* @param lock
* Lock to use during operation.
* @param condition
* Condition to wait on when there s nothing to process.
*/
public AbstractBufferElementProcessor(AtomicBuffer<E> atomicBuffer, AtomicReference<IBufferElement<E>> lastProcessed, Lock lock, Condition condition) {
this.atomicBuffer = atomicBuffer;
this.lastProcessed = lastProcessed;
this.lock = lock;
this.condition = condition;
}
/**
* Processes next element to be processed. Note that this method passes the element to the
* {@link #process(IBufferElement, IBufferElement)} method so that sub-classes can execute the
* real processing. This method handles waiting of element to be available for processing.
*
* @throws InterruptedException
* If {@link InterruptedException} occurs.
*/
public void process() throws InterruptedException {
// wait until there are elements to process
// we wait if:
// 1) queue is empty -> last points to empty element
// 2) all are analyzed -> last to process is not empty element, but points to the empty
// one
while (true) {
IBufferElement<E> lastProcessedElement = lastProcessed.get();
if ((this.atomicBuffer.emptyBufferElement == this.atomicBuffer.last.get())
|| ((this.atomicBuffer.emptyBufferElement != lastProcessedElement) && (this.atomicBuffer.emptyBufferElement == lastProcessedElement.getNextElement()))) { // NOPMD
lock.lock();
try {
// check again with lock
lastProcessedElement = this.atomicBuffer.lastAnalyzed.get();
if ((this.atomicBuffer.emptyBufferElement == this.atomicBuffer.last.get())
|| ((this.atomicBuffer.emptyBufferElement != lastProcessedElement) && (this.atomicBuffer.emptyBufferElement == lastProcessedElement.getNextElement()))) { // NOPMD
condition.await();
} else {
break;
}
} finally {
lock.unlock();
}
} else {
break;
}
}
while (true) {
this.atomicBuffer.clearReadLock.lock();
try {
IBufferElement<E> elementToProcess = null;
IBufferElement<E> lastProcessElement = lastProcessed.get();
// if last analyzed points to empty then we take the first added element
if (this.atomicBuffer.emptyBufferElement == lastProcessElement) { // NOPMD
elementToProcess = this.atomicBuffer.last.get();
} else {
elementToProcess = lastProcessElement.getNextElement();
}
// if there is nothing to analyze any more break
if (this.atomicBuffer.emptyBufferElement == elementToProcess) { // NOPMD
break;
}
if (process(elementToProcess, lastProcessElement)) {
break;
}
} finally {
this.atomicBuffer.clearReadLock.unlock();
}
}
}
/**
* Sub-classes should implement this method with the real processing.
*
* @param elementToProcess
* Element to be processed.
* @param lastProcessedElement
* Last successfully processed element.
* @return This method should return <code>true</code> if element was processed and
* <code>false</code> otherwise.
*/
public abstract boolean process(IBufferElement<E> elementToProcess, IBufferElement<E> lastProcessedElement);
}