/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.batchindexing.impl;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Implements a blocking queue capable of storing
* a "poison" token to signal consumer threads
* that the task is finished.
*
* @author Sanne Grinovero
*/
public class ProducerConsumerQueue<T> {
private static final int DEFAULT_BUFF_LENGTH = 1000;
private static final Object exitToken = new Object();
//doesn't use <T> here as exitToken needs to be put in the queue too:
private final BlockingQueue<Object> queue;
private final AtomicInteger producersToWaitFor;
/**
* @param producersToWaitFor The number of producer threads.
*/
public ProducerConsumerQueue( int producersToWaitFor ) {
this( DEFAULT_BUFF_LENGTH, producersToWaitFor );
}
public ProducerConsumerQueue( int queueLength, int producersToWaitFor ) {
queue = new ArrayBlockingQueue<Object>( queueLength );
this.producersToWaitFor = new AtomicInteger( producersToWaitFor );
}
/**
* Blocks until an object is available; when null
* is returned the client thread should quit.
* @return the next object in the queue, or null to exit
* @throws InterruptedException if interrupted while waiting
*/
@SuppressWarnings("unchecked")
public T take() throws InterruptedException {
Object obj = queue.take();
if ( obj == exitToken ) {
//restore exit signal for other threads
queue.put( exitToken );
return null;
}
else {
return (T)obj;
}
}
/**
* Adds a new object to the queue, blocking if no space is
* available.
* @param obj the object to add to the queue
* @throws InterruptedException if interrupted while waiting
*/
public void put(T obj) throws InterruptedException {
queue.put( obj );
}
/**
* Each producer thread should call producerStopping() when it has
* finished. After doing it can safely terminate.
* After all producer threads have called producerStopping()
* a token will be inserted in the blocking queue to eventually
* awake sleeping consumers and have them quit, after the
* queue has been processed.
*/
public void producerStopping() {
int activeProducers = producersToWaitFor.decrementAndGet();
//last producer must close consumers
if ( activeProducers == 0 ) {
try {
queue.put( exitToken );//awake all waiting threads to let them quit.
}
catch (InterruptedException e) {
//just quit, consumers will be interrupted anyway if it's a shutdown.
Thread.currentThread().interrupt();
}
}
}
}