package voldemort.store.routed.action;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
import voldemort.cluster.Node;
import voldemort.store.routed.Response;
import voldemort.utils.ByteArray;
/**
* The AsyncPutSynchronizer Class is used for synchronizing operations inside
* PerformParallelPut action More specifically, it coordinate the exception
* handling and hinted handoff responsibility between master thread and async
* put threads
*
*/
public class AsyncPutSynchronizer {
private final static Logger logger = Logger.getLogger(AsyncPutSynchronizer.class);
private boolean asyncCallbackShouldSendhint;
private boolean responseHandlingCutoff;
private final ConcurrentMap<Node, Boolean> slopDestinations; // the value in
// the map is
// not used
private final Queue<Response<ByteArray, Object>> responseQueue;
public AsyncPutSynchronizer() {
asyncCallbackShouldSendhint = false;
responseHandlingCutoff = false;
slopDestinations = new ConcurrentHashMap<Node, Boolean>();
responseQueue = new LinkedList<Response<ByteArray, Object>>();
}
/**
* Get list of nodes to register slop for
*
* @return list of nodes to register slop for
*/
public synchronized Set<Node> getDelegatedSlopDestinations() {
return Collections.unmodifiableSet(slopDestinations.keySet());
}
/**
* Stop accepting delegated slop responsibility by master
*/
public synchronized void disallowDelegateSlop() {
asyncCallbackShouldSendhint = true;
}
/**
* Try to delegate the responsibility of sending slops to master
*
* @param node The node that slop should eventually be pushed to
* @return true if master accept the responsibility; false if master does
* not accept
*/
public synchronized boolean tryDelegateSlop(Node node) {
if(asyncCallbackShouldSendhint) {
return false;
} else {
slopDestinations.put(node, true);
return true;
}
}
/**
* Master Stop accepting new responses (from async callbacks)
*/
public synchronized void cutoffHandling() {
responseHandlingCutoff = true;
}
/**
* try to delegate the master to handle the response
*
* @param response
* @return true if the master accepted the response; false if the master
* didn't accept
*/
public synchronized boolean tryDelegateResponseHandling(Response<ByteArray, Object> response) {
if(responseHandlingCutoff) {
return false;
} else {
responseQueue.offer(response);
this.notifyAll();
return true;
}
}
/**
* poll the response queue for response
*
* @param timeout timeout amount
* @param timeUnit timeUnit of timeout
* @return same result of BlockQueue.poll(long, TimeUnit)
* @throws InterruptedException
*/
public synchronized Response<ByteArray, Object> responseQueuePoll(long timeout,
TimeUnit timeUnit)
throws InterruptedException {
long timeoutMs = timeUnit.toMillis(timeout);
long timeoutWallClockMs = System.currentTimeMillis() + timeoutMs;
while(responseQueue.isEmpty() && System.currentTimeMillis() < timeoutWallClockMs) {
long remainingMs = Math.max(0, timeoutWallClockMs - System.currentTimeMillis());
if(logger.isDebugEnabled()) {
logger.debug("Start waiting for response queue with timeoutMs: " + timeoutMs);
}
this.wait(remainingMs);
if(logger.isDebugEnabled()) {
logger.debug("End waiting for response queue with timeoutMs: " + timeoutMs);
}
}
return responseQueue.poll();
}
/**
* to see if the response queue is empty
*
* @return true is response queue is empty; false if not empty.
*/
public synchronized boolean responseQueueIsEmpty() {
return responseQueue.isEmpty();
}
}