package htsjdk.samtools.cram.paralell;
import htsjdk.samtools.util.Log;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* A class similar to a blocking queue that can be closed. Conveyer is closed
* when all registered suppliers have called close() method and the underlying
* queue is empty. Effectively this should guarantee that no new objects will
* appear on the conveyer and all consumers should shutdown normally.
*
* @author vadim
*
* @param <T>
*/
class Conveyer<T> {
private static Log log = Log.getInstance(Conveyer.class);
protected BlockingQueue<T> queue;
private AtomicInteger countDownToClose = new AtomicInteger(0);
protected AtomicInteger counter = new AtomicInteger(0);
/**
* Create a new conveyer with the given number of suppliers.
*
* @param queue
* a delegate queue to use
* @param nofSuppliers
* number of suppliers to the conveyer
*/
Conveyer(BlockingQueue<T> queue, int nofSuppliers) {
this.queue = queue;
this.countDownToClose.set(nofSuppliers);
}
static <T> Conveyer<T> create(int queueCapacity, int suppliers) {
return new Conveyer<T>(new ArrayBlockingQueue<T>(queueCapacity), suppliers);
}
static <T> Conveyer<T> createWithQueueCapacity(int queueCapacity) {
return new Conveyer<T>(new ArrayBlockingQueue<T>(queueCapacity), 0);
}
void close() {
log.info("close: " + countDownToClose.decrementAndGet());
}
void addSupplier() {
countDownToClose.incrementAndGet();
}
/**
* Check if the conveyer has processed everything.
*
* @return true if there nothing else to process, false otherwise
*/
boolean hasCompleted() {
return countDownToClose.get() <= 0 && queue.isEmpty();
}
int size() {
return queue.size();
}
boolean isEmpty() {
return queue.isEmpty();
}
void put(T object) throws InterruptedException {
if (hasCompleted())
throw new IllegalStateException();
queue.put(object);
}
T peek() {
return queue.peek();
}
int remainingCapacity() {
return queue.remainingCapacity();
}
/**
* Try to pop an object from the conveyer. This is a semi-blocking
* implementation: it repeatedly blocks and checks if the conveyer has
* completed.
*
* @return an object to process or null if everything has been processed
* @throws InterruptedException
* as per Java IO contract due to blocking queue polling.
*/
T tryAdvance() throws InterruptedException {
if (hasCompleted())
return null;
T poll = null;
while (!hasCompleted() && (poll = queue.poll(100, TimeUnit.MILLISECONDS)) == null)
;
if (poll == null)
return null;
counter.incrementAndGet();
return poll;
}
@Override
public String toString() {
return String.format("[%s, suppliers=%d, queued=%d, processed=%d]", hasCompleted() ? "completed" : "active",
countDownToClose.get(), queue.size(), counter.get());
}
}