package ibis.ipl.impl.stacking.lrmc.io; import ibis.ipl.impl.stacking.lrmc.util.Message; import ibis.ipl.impl.stacking.lrmc.util.MessageCache; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; //import mcast.lrm.ByteArrayCache; public class LrmcInputStream extends InputStream { private static final Logger logger = LoggerFactory .getLogger(LrmcInputStream.class); private final int source; private final ArrayList<Message> queue = new ArrayList<Message>(); private Message current = null; private int index = 0; private int currentID = 0; private int currentNum = 0; private boolean finish = false; // private long memoryUsage = 0; // private int lowBound = 0; // private int highBound = 1024*1024; private MessageCache cache; public LrmcInputStream(int source, MessageCache cache) { this.source = source; this.cache = cache; } public synchronized void terminate() { finish = true; notifyAll(); } public int getSource() { return source; } public boolean haveData() { if (current != null && index < current.len) { return true; } synchronized (this) { return queue.size() != 0; } } public boolean addMessage(Message m) { synchronized (this) { queue.add(m); notify(); } if (logger.isDebugEnabled()) { logger.debug("Queued message " + m.id + "/" + m.num + "(" + m.len + ")"); } return true; } private void getMessage() { synchronized (this) { while (!finish && queue.size() == 0) { try { wait(); } catch (Exception e) { // ignored } } if (finish) { current = null; return; } current = queue.remove(0); } index = 0; if (logger.isDebugEnabled()) { logger.debug("Dequeued message " + current.id + "/" + current.num + "(" + current.len + ")"); } } private void checkMessage() throws IOException { // Check if the ID/number of the packet corresponds to what we expect. if (currentID == 0) { // We must start a new series of packets here. Each series // corresponds to a 'multi fragment' message. while (current.num != 0) { // Oh dear, we seem to have missed the start of a series of // packets. We may have lost a message somewhere (which is // unlikely) or the last receive failed half way due to some // deserialization problem and the stream is now confused a bit // (this is more likely). To solve this, we simply drop the // current packet and get a new one. We keep trying until we see // a 'first packet'. logger.info("___ Dropping packet " + current.id + "/" + current.num + " [" + current.len + "] " + "since it's not the first one!"); freeMessage(); getMessage(); if (current == null) { return; } } if (logger.isDebugEnabled()) { logger.debug("Starting new series " + current.id); } currentID = current.id; currentNum = 0; /* * if (memoryUsage > highBound) { System.err.println("++++ Current * memory usage " + (memoryUsage/1024) + " KB, series " + * currentID); * * lowBound = highBound; highBound += 1024*1024; } else if * (memoryUsage < lowBound) { * * System.err.println("---- Current memory usage " + * (memoryUsage/1024) + " KB, series " + currentID); * * highBound = lowBound; lowBound -= 1024*1024; } */ } else if (currentID != current.id || currentNum != current.num) { // Oh dear, we seem to have missed a part of a series of packets. // This is likely to happen when one of our predecessors in the // multicast chain has crashed. As a result, it does not forward the // one or more packets to me. When it's predecesor notices this, it // may change the chain and start sending to me directly. This way, // we see a sudden change in the ID number, without having seen a // 'last packet' for the previous series, or we see the currentNum // skip a few values. // We solve this setting the currentID to 0 (to indicate that we // want to start a new series) and throwing an exception to notify // the user that we cannot finish the current multicast. We will // process the current message when the user has handled the // exception and tries to receive again. String tmp = "Inconsistency discovered in multicast packet series," + " current series " + currentID + "/" + currentNum + " next packet " + current.id + "/" + current.num; currentID = 0; throw new IOException(tmp); } } private void freeMessage() { // Free the current message and updates the currentID if necessary if (logger.isDebugEnabled()) { logger.debug("Freed message " + current.id + "/" + current.num + " last = " + current.last); } if (current.last) { currentID = 0; currentNum = 0; } else { currentNum++; } // Note use real length here! // memoryUsage -= current.buffer.length; cache.put(current); current = null; } public int read(byte b[], int off, int len) throws IOException { if (current == null) { getMessage(); } if (current == null) { throw new IOException("Someone wants us to stop ..."); } checkMessage(); if (current == null) { throw new IOException("Someone wants us to stop ..."); } int leftover = current.len - index; if (leftover <= len) { if (logger.isDebugEnabled()) { logger.debug("Copying " + index + " " + current.buffer.length + " " + off + " " + leftover); } System.arraycopy(current.buffer, index, b, off, leftover); freeMessage(); return leftover; } else { System.arraycopy(current.buffer, index, b, off, len); index += len; return len; } } public int read() throws IOException { // Ouch ... fortunately it's never used! byte[] tmp = new byte[1]; read(tmp, 0, 1); return tmp[0]; } }