package ibis.ipl.impl.stacking.lrmc;
import ibis.ipl.impl.stacking.lrmc.io.LrmcInputStream;
import ibis.ipl.impl.stacking.lrmc.util.Message;
import ibis.ipl.impl.stacking.lrmc.util.MessageCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class InputStreams {
private static final int DEFAULT_SIZE = 64;
private static final Logger logger = LoggerFactory.getLogger(InputStreams.class);
private LrmcInputStream[] inputStreams = new LrmcInputStream[DEFAULT_SIZE];
private boolean[] hasData = new boolean[DEFAULT_SIZE];
private boolean[] busy = new boolean[DEFAULT_SIZE];
private int streamsWithData = 0;
private int index = 0;
private int last = -1;
private boolean finish = false;
private void add(LrmcInputStream is, int sender) {
if (sender >= inputStreams.length) {
resize(sender);
}
inputStreams[sender] = is;
if (sender > last) {
last = sender;
}
}
public synchronized void terminate() {
finish = true;
for (int i = 0; i <= last; i++) {
if (inputStreams[i] != null) {
inputStreams[i].terminate();
}
}
notifyAll();
}
private void resize(int minimumSize) {
int newSize = hasData.length;
while (newSize <= minimumSize) {
newSize *= 2;
}
LrmcInputStream[] tmp1 = new LrmcInputStream[newSize];
System.arraycopy(inputStreams, 0, tmp1, 0, inputStreams.length);
inputStreams = tmp1;
boolean[] tmp2 = new boolean[newSize];
System.arraycopy(hasData, 0, tmp2, 0, hasData.length);
hasData = tmp2;
boolean[] tmp3 = new boolean[newSize];
System.arraycopy(busy, 0, tmp3, 0, busy.length);
busy = tmp3;
}
public synchronized LrmcInputStream get(int sender, MessageCache cache) {
LrmcInputStream tmp = find(sender);
if (tmp == null) {
tmp = new LrmcInputStream(sender, cache);
add(tmp, sender);
}
return tmp;
}
private LrmcInputStream find(int sender) {
if (sender < 0 || sender > last) {
return null;
}
return inputStreams[sender];
}
public synchronized void returnStream(LrmcInputStream is) {
busy[is.getSource()] = false;
if (is.haveData()) {
if (logger.isDebugEnabled()) {
logger.debug("return stream " + is.getSource()
+ ", still has data");
}
hasData(is);
} else {
if (logger.isDebugEnabled()) {
logger.debug("return stream " + is.getSource()
+ ", no data left");
}
}
}
public synchronized boolean hasData(LrmcInputStream is, Message m) {
if (is.addMessage(m)) {
hasData(is);
return true;
}
return false;
}
public synchronized void hasData(LrmcInputStream is) {
int src = is.getSource();
if (!hasData[src] && !busy[src]) {
// Fix: Test before setting and incrementing counter (Ceriel)
// Fix: Don't set hasData while it is busy. This may be incorrect
// when we are still reading from the stream. We will see if
// there is new data when we return the stream. (Ceriel)
hasData[src] = true;
if (logger.isDebugEnabled()) {
logger.debug("Setting hasData for stream " + src);
if (!is.haveData()) {
logger.debug("Set hasData but no data?", new Throwable());
}
}
streamsWithData++;
if (streamsWithData == 1) {
notifyAll();
}
}
}
public synchronized LrmcInputStream getNextFilledStream() {
while (!finish && streamsWithData == 0) {
try {
wait();
} catch (Exception e) {
// ignored
}
}
if (finish) {
return null;
}
final int size = inputStreams.length;
for (int i = 1; i <= size; i++) {
if (hasData[(index + i) % size]) {
index = (index + i) % size;
break;
}
}
if (logger.isDebugEnabled() && !hasData[index]) {
logger.debug("GetNextFilledStream returns !hasData stream"
+ ", streamsWithData = " + streamsWithData + ", index = "
+ index, new Throwable());
}
hasData[index] = false;
if (logger.isDebugEnabled()) {
logger.debug("start read from stream " + index);
}
busy[index] = true;
if (logger.isDebugEnabled() && !inputStreams[index].haveData()) {
logger.debug("GetNextFilledStream returns empty stream"
+ ", streamsWithData = " + streamsWithData + ", index = "
+ index, new Throwable());
}
streamsWithData--;
return inputStreams[index];
}
}