package ibis.ipl.impl.stacking.lrmc;
import ibis.io.SerializationFactory;
import ibis.io.SerializationInput;
import ibis.io.SerializationOutput;
import ibis.ipl.IbisIdentifier;
import ibis.ipl.PortType;
import ibis.ipl.impl.stacking.lrmc.io.BufferedArrayInputStream;
import ibis.ipl.impl.stacking.lrmc.io.BufferedArrayOutputStream;
import ibis.ipl.impl.stacking.lrmc.io.LrmcInputStream;
import ibis.ipl.impl.stacking.lrmc.io.LrmcOutputStream;
import ibis.ipl.impl.stacking.lrmc.io.MessageReceiver;
import ibis.ipl.impl.stacking.lrmc.util.Message;
import ibis.ipl.impl.stacking.lrmc.util.MessageCache;
import ibis.util.TypedProperties;
import java.io.IOException;
// TODO find a way to share destination arrays in messages --Rob
public class Multicaster implements MessageReceiver {
private final int MESSAGE_SIZE;
private final int MESSAGE_CACHE_SIZE;
LabelRoutingMulticast lrmc;
final PortType portType;
final String name;
private LrmcOutputStream os;
BufferedArrayOutputStream bout;
BufferedArrayInputStream bin;
SerializationOutput sout;
SerializationInput sin;
private long totalData = 0;
private long lastBytesWritten = 0;
private MessageCache cache;
private boolean finish = false;
private boolean receiverDone = false;
private Thread receiver = null;
private InputStreams inputStreams = new InputStreams();
private IbisIdentifier[] destination = null;
LrmcSendPort sendPort = null;
LrmcReceivePort receivePort = null;
public Multicaster(LrmcIbis ibis, PortType type, String name)
throws IOException {
TypedProperties tp = new TypedProperties(ibis.properties());
this.MESSAGE_SIZE = tp.getIntProperty("lrmc.messageSize", 8 * 1024);
this.MESSAGE_CACHE_SIZE = tp.getIntProperty("lrmc.messageCacheSize",
1500);
cache = new MessageCache(MESSAGE_CACHE_SIZE, MESSAGE_SIZE);
lrmc = new LabelRoutingMulticast(ibis, this, cache, name);
os = new LrmcOutputStream(lrmc, cache);
bout = new BufferedArrayOutputStream(os, MESSAGE_SIZE);
bin = new BufferedArrayInputStream(null);
String serialization;
if (type.hasCapability(PortType.SERIALIZATION_DATA)) {
serialization = "data";
} else if (type.hasCapability(PortType.SERIALIZATION_OBJECT_SUN)) {
serialization = "sun";
} else if (type.hasCapability(PortType.SERIALIZATION_OBJECT_IBIS)) {
serialization = "ibis";
} else if (type.hasCapability(PortType.SERIALIZATION_OBJECT)) {
serialization = "object";
} else {
serialization = "byte";
}
sout = SerializationFactory.createSerializationOutput(serialization,
bout);
sin = SerializationFactory.createSerializationInput(serialization, bin);
portType = type;
this.name = name;
}
public synchronized void setDestination(IbisIdentifier[] dest) {
destination = dest;
cache.setDestinationSize(dest.length);
}
public void gotDone(int id) {
// nothing
}
public boolean gotMessage(Message m) {
LrmcInputStream tmp;
// Fix: combined find call and add call into get().
// There was a race here. (Ceriel)
tmp = inputStreams.get(m.sender, cache);
// tmp.addMessage(m);
// inputStreams.hasData(tmp);
// Fix: avoid race: message may have been read before setting
// hasData. (Ceriel)
return inputStreams.hasData(tmp, m);
}
void initializeSend(IbisIdentifier[] destinations) throws IOException {
if (destination != destinations) {
destination = destinations;
lrmc.setDestination(destinations);
}
// We only want to return the number of bytes written in this bcast, so
// reset the count.
bout.resetBytesWritten();
os.reset();
sout.reset(true);
}
long finalizeSend() throws IOException {
sout.flush();
bout.forcedFlush();
lastBytesWritten = bout.bytesWritten();
totalData += lastBytesWritten;
return lastBytesWritten;
}
long finalizeRead(LrmcInputStream stream) {
inputStreams.returnStream(stream);
long sz = bin.bytesRead();
totalData += sz;
return sz;
}
public LrmcReadMessage receive() {
synchronized (this) {
if (finish) {
return null;
}
receiverDone = false;
receiver = Thread.currentThread();
}
LrmcReadMessage o;
try {
LrmcInputStream stream = inputStreams.getNextFilledStream();
if (stream == null) {
return null;
}
// Plug it into the deserializer
bin.setInputStream(stream);
bin.resetBytesRead();
o = new LrmcReadMessage(this, stream);
} finally {
synchronized (this) {
receiverDone = true;
if (finish) {
notifyAll();
return null;
}
receiver = null;
}
}
return o;
}
public long bytesRead() {
return bin.bytesRead();
}
public long bytesWritten() {
return bout.bytesWritten();
}
public long totalBytes() {
return totalData;
}
public void done() {
synchronized (this) {
finish = true;
inputStreams.terminate();
notifyAll();
// we can tell the receiver thread, but we don't know that
// it will actually finish, so we cannot join it.
if (receiver != null) {
// Wait until this is noticed.
while (!receiverDone) {
try {
wait();
} catch (Exception e) {
// What to do here? (TODO)
}
}
}
}
try {
os.close();
// sout.close(); // don't close this one. It keeps on talking...
sin.close();
lrmc.done();
} catch (IOException e) {
// ignore, we tried ...
}
}
public void removeReceivePort() {
receivePort = null;
if (sendPort == null) {
// Apparently we are done ...
done();
}
}
public void removeSendPort() {
sendPort = null;
if (receivePort == null) {
// Apparently we are done ...
done();
}
}
}