/*
* ModeShape (http://www.modeshape.org)
*
* 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 org.modeshape.common.collection.ring;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author Randall Hauch (rhauch@redhat.com)
*/
public class GarbageCollectingConsumer implements Runnable, AutoCloseable, DependentOnPointers {
public static interface Collectable {
void collect( long position );
}
private final Cursor cursor;
private final PointerBarrier gcBarrier;
private final Pointer pointer;
protected final TrailingPointer trailingPointer;
private final AtomicBoolean runThread = new AtomicBoolean(true);
private final CountDownLatch stopLatch = new CountDownLatch(1);
private final Collectable collectable;
public GarbageCollectingConsumer( final Cursor cursor,
Pointer cursorPointer,
final WaitStrategy waitStrategy,
Collectable collectable ) {
this.cursor = cursor;
this.pointer = new Pointer();
this.trailingPointer = new TrailingPointer(cursorPointer);
this.cursor.stayBehind(this.pointer);
this.collectable = collectable;
// Create the pointer barrier that will be called only when used ...
this.gcBarrier = new PointerBarrier() {
private volatile boolean closed = false;
@Override
public long waitFor( long position ) throws InterruptedException, TimeoutException {
if (cursor.isComplete()) {
// The cursor is done, so return -1 ...
return -1;
}
// Use the gcPointer instead ...
long availableSequence = waitStrategy.waitFor(position, trailingPointer, trailingPointer, this);
if (availableSequence < position) {
return availableSequence;
}
return cursor.getHighestPublishedPosition(position, availableSequence);
}
@Override
public boolean isComplete() {
return closed || cursor.isComplete();
}
@Override
public void close() {
this.closed = true;
}
};
}
@Override
public void run() {
try {
while (runThread.get()) {
long next = pointer.get() + 1L;
try {
// Try to find the next position we can read to ...
long maxPosition = gcBarrier.waitFor(next);
while (next <= maxPosition) {
collectable.collect(next);
if (!runThread.get()) return;
next = pointer.incrementAndGet() + 1L;
}
if (maxPosition < 0) {
// The buffer has been shutdown and there are no more positions, so we're done ...
return;
}
} catch (TimeoutException e) {
// It took too long to wait, but just continue ...
} catch (InterruptedException e) {
// The thread was interrupted ...
Thread.interrupted();
break;
} catch (RuntimeException e) {
// Don't retry this entry, so just advance the pointer and continue ...
pointer.incrementAndGet();
}
}
} finally {
// When we're done, so tell the cursor to ignore our pointer ...
try {
cursor.ignore(pointer);
} finally {
stopLatch.countDown();
}
}
}
@Override
public void close() {
if (this.runThread.compareAndSet(true, false)) {
try {
this.stopLatch.await();
} catch (InterruptedException e) {
// do nothing ...
}
}
}
@Override
public boolean ignore( Pointer pointer ) {
return trailingPointer.ignore(pointer);
}
@Override
public void stayBehind( Pointer... pointers ) {
trailingPointer.stayBehind(pointers);
}
}