/**
* CopyRight by Chinamobile
*
* Sender.java
*/
package com.chinamobile.bcbsp.comm;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.chinamobile.bcbsp.api.Combiner;
import com.chinamobile.bcbsp.util.BSPJobID;
/**
* Sender
*
* A sender belongs to a communicator, for sending
* messages from the outgoing queues.
*
* @author
* @version
*/
public class Sender extends Thread {
// For log
private static final Log LOG = LogFactory.getLog(Sender.class);
// For time
private Long connectTime = 0L;/**Clock*/
private Long sendTime = 0L;/**Clock*/
private boolean condition = false;
BSPJobID jobID = null;
private int messageCount = 0; // Total count.
private Communicator communicator = null;
private MessageQueuesInterface messageQueues = null;
boolean combinerFlag = false;
private Combiner combiner = null;
private CombinerTool combinerTool = null;
private int combineThreshold;
private int sendThreshold;
private int packSize;
private int maxProducerNum;
private boolean noMoreMessagesFlag = false;
private ProducerPool producerPool = null;
//The current progress.
private volatile int superStepCounter = 0;
//The flag for a super step.
private volatile boolean over = false;
//The flag for the whole staff.
private volatile boolean completed = false;
public Sender(Communicator comm) {
this.communicator = comm;
this.sendThreshold = this.communicator.getJob().getSendThreshold();
this.packSize = this.communicator.getJob().getMessagePackSize();
this.jobID = this.communicator.getBSPJobID();
this.messageQueues = this.communicator.getMessageQueues();
this.combinerFlag = this.communicator.isSendCombineSetFlag();
// Get the combiner from the communicator.
if (combinerFlag) {
this.combiner = this.communicator.getCombiner();
this.combineThreshold = this.communicator.getJob().getSendCombineThreshold();
}
this.maxProducerNum = this.communicator.getJob().getMaxProducerNum();
this.superStepCounter = 0;
}
public void setNoMoreMessagesFlag(boolean flag) {
this.noMoreMessagesFlag = flag;
}
public boolean getNoMoreMessagesFlag() {
return this.noMoreMessagesFlag;
}
public void setSendThreshold(int aSendThreshold) {
if (this.sendThreshold == 0) {
this.sendThreshold = aSendThreshold;
}
}
public void setCombineThreshold(int aCombineThreshold) {
if (this.combineThreshold == 0) {
this.combineThreshold = aCombineThreshold;
}
}
public void setMessagePackSize(int size) {
if (this.packSize == 0) {
this.packSize = size;
}
}
public void setMaxProducerNum(int num) {
if (this.maxProducerNum == 0) {
this.maxProducerNum = num;
}
}
/**Clock*/
public void addConnectTime(long time) {
synchronized (this.connectTime) {
this.connectTime += time;
}
}
/**Clock*/
public void addSendTime(long time) {
synchronized (this.sendTime) {
this.sendTime += time;
}
}
public void run() {
try {
LOG.info("[Sender] starts successfully!");
this.initialize();
synchronized(this) {
try {
while (!this.condition) {
wait(); //wait for first begin.
}
this.condition = false;
} catch (InterruptedException e) {
LOG.error("[Sender] caught: ", e);
}
}
do { // while for totally complete for this staff.
LOG.info("[Sender] begins to work for sueper step <" + this.superStepCounter + ">!");
this.connectTime = 0L;/**Clock*/
this.sendTime = 0L;/**Clock*/
ProducerTool producer = null;
if (combinerFlag && !this.noMoreMessagesFlag) {
this.combinerTool = new CombinerTool(this, messageQueues, combiner, combineThreshold);
this.combinerTool.start();
}
String outgoingIndex = null;
ConcurrentLinkedQueue<BSPMessage> maxQueue = null;
int maxSize = 0;
while (true) { // Keep sending until the whole outgoingQueues is empty
// and no more messages will come.
maxSize = 0;
maxQueue = null;
//outgoingIndex = this.messageQueues.getMaxOutgoingQueueIndex();
outgoingIndex = this.messageQueues.getNextOutgoingQueueIndex();
if (outgoingIndex != null) {
maxSize = this.messageQueues.getOutgoingQueueSize(outgoingIndex);
}
// When the whole outgoingQueues are empty and no more messages will
// come, exit while.
if (outgoingIndex == null) {
if (this.noMoreMessagesFlag) {
if (this.messageQueues.getOutgoingQueuesSize() > 0) {
continue;
}
if (!this.combinerFlag) {
/** no more messages and no combiner set*/
LOG.info("[Sender] exits while for no more messages and no combiner set.");
break;
}
else {
if (this.combinerTool == null) {
/** no more messages and no combiner created*/
LOG.info("[Sender] exits while for no more messages and no combiner created.");
break;
} else if (!this.combinerTool.isAlive()){
/** no more messages and combiner has exited*/
LOG.info("[Sender] exits while for no more messages and combiner has exited.");
break;
}
}
} else {
try {
Thread.sleep(500); // Wait for 500ms until next check.
} catch (Exception e) {
LOG.error("[Sender] caught: ", e);
}
continue;
}
}
else if (maxSize > this.sendThreshold || this.noMoreMessagesFlag) {
// Get a producer tool to send the maxQueue
producer = this.producerPool.getProducer(outgoingIndex, this.superStepCounter, this);
if (producer.isIdle()) {
// Get the queue out of the map.
maxQueue = this.messageQueues.removeOutgoingQueue(outgoingIndex);
if (maxQueue == null)
continue;
maxSize = maxQueue.size();
producer.setPackSize(this.packSize);
producer.addMessages(maxQueue);
this.messageCount += maxSize;
// Set the producer's state to busy to start it.
producer.setIdle(false);
}
if (producer.isFailed()) {
this.messageQueues.removeOutgoingQueue(outgoingIndex);
}
}
} // while
LOG.info("[Sender] has started " + producerPool.getProducerCount() + " producers totally.");
//Tell all producers that no more messages.
this.producerPool.finishAll();
while (true) { // Wait for the producers finish message sending.
int running = this.producerPool.getActiveProducerCount(this.superStepCounter);
LOG.info("[Sender] There are still <" + running + "> ProducerTools alive.");
if (running == 0) {
break;
}
try {
Thread.sleep(500); //Wait for 500ms until next check.
} catch (Exception e) {
LOG.error("[Sender] caught: ", e);
}
} // while
LOG.info("[Sender] has sent " + this.messageCount + " messages totally for super step <"
+ this.superStepCounter + ">! And enter waiting......");
LOG.info("[==>Clock<==] <Sender's create connection> used " + this.connectTime/1000f + " seconds");/**Clock*/
LOG.info("[==>Clock<==] <Sender's send messages> used " + this.sendTime/1000f + " seconds");/**Clock*/
this.over = true;
synchronized(this) {
try {
while (!this.condition) {
wait();// wait for next begin.
}
this.condition = false;
} catch (InterruptedException e) {
LOG.error("[Sender] caught: ", e);
}
}
} while (!this.completed);
LOG.info("[Sender] exits.");
} catch (Exception e) {
LOG.error("Sender caught: ", e);
}
} // run
private void initialize() {
LOG.info("========== Initialize Sender ==========");
LOG.info("[Send Threshold] = " + this.sendThreshold);
LOG.info("[Message Pack Size] = " + this.packSize);
if (this.combinerFlag) {
LOG.info("[Combine Threshold for sending] = " + this.combineThreshold);
}
LOG.info("[Max Producer Number] = " + this.maxProducerNum);
LOG.info("=======================================");
this.producerPool = new ProducerPool(this.maxProducerNum, this.jobID.toString());
}
/**
* Begin the sender's task for a super step.
*
* @param superStepCount
*/
public void begin(int superStepCount) {
this.superStepCounter = superStepCount;
if (this.producerPool == null) {
this.producerPool = new ProducerPool(this.maxProducerNum, this.jobID.toString());
LOG.error("Test Null this.producePool is null and it is re-initialized");
}
this.producerPool.setActiveProducerProgress(superStepCount);
this.producerPool.cleanFailedProducer();
this.noMoreMessagesFlag = false;
this.over = false;
this.messageCount = 0;
synchronized(this) {
this.condition = true;
notify();
}
}
/**
* Justify if the sender has finished the task for the current super step.
*
* @return over
*/
public boolean isOver() {
return this.over;
}
/**
* To notice the sender to complete and return.
*/
public void complete() {
this.producerPool.completeAll();
this.completed = true;
synchronized(this) {
this.condition = true;
notify();
}
}
}