package edu.washington.escience.myria.parallel.ipc;
import org.jboss.netty.channel.Channel;
import com.google.common.base.Preconditions;
import edu.washington.escience.myria.operator.network.Consumer;
import edu.washington.escience.myria.operator.network.Producer;
import edu.washington.escience.myria.util.concurrent.ReentrantSpinLock;
import edu.washington.escience.myria.util.concurrent.ThreadStackDump;
/**
* The data structure recording the logical role of {@link StreamInputChannel} and {@link StreamOutputChannel} that an
* IO channel plays.
* <p>
* An IO channel can be an input of a {@link Consumer} operator (inputChannel) and in the same time an output of a
* {@link Producer} operator (outputChannel).
* */
class StreamIOChannelPair {
/** The logger for this class. */
private static final org.slf4j.Logger LOGGER =
org.slf4j.LoggerFactory.getLogger(StreamIOChannelPair.class);
/**
* The lock protecting the consistency of input flow control setup.
* */
private final ReentrantSpinLock inputMappingLock = new ReentrantSpinLock();
/**
* The lock protecting the consistency of output flow control setup.
* */
private final ReentrantSpinLock outputMappingLock = new ReentrantSpinLock();
/**
* The input stream channel.
* */
private StreamInputChannel<?> inputStreamChannel;
/**
* The output stream channel.
* */
private StreamOutputChannel<?> outputStreamChannel;
/**
* Owner channel context. A StreamIOChannelPair must be attached to a channel.
* */
private final ChannelContext ownerChannelContext;
/**
* @param ownerCTX owner ChannelContext.
* */
StreamIOChannelPair(final ChannelContext ownerCTX) {
ownerChannelContext = Preconditions.checkNotNull(ownerCTX);
}
/**
* @return the input channel in the pair.
* @param <PAYLOAD> the payload type.
* */
@SuppressWarnings("unchecked")
final <PAYLOAD> StreamInputChannel<PAYLOAD> getInputChannel() {
inputMappingLock.lock();
try {
return (StreamInputChannel<PAYLOAD>) inputStreamChannel;
} finally {
inputMappingLock.unlock();
}
}
/**
* @return the output channel in the pair.
* @param <PAYLOAD> the payload type.
* */
@SuppressWarnings("unchecked")
final <PAYLOAD> StreamOutputChannel<PAYLOAD> getOutputChannel() {
outputMappingLock.lock();
try {
return (StreamOutputChannel<PAYLOAD>) outputStreamChannel;
} finally {
outputMappingLock.unlock();
}
}
/**
* Link the logical inputChannel with the physical ioChannel.
*
* @param inputChannel the logical channel.
* */
final void mapInputChannel(final StreamInputChannel<?> inputChannel) {
Preconditions.checkNotNull(inputChannel);
Channel ioChannel = ownerChannelContext.getChannel();
inputMappingLock.lock();
try {
if (inputStreamChannel != null) {
throw new IllegalStateException(
"Physical channel "
+ ChannelContext.channelToString(ownerChannelContext.getChannel())
+ " already attached to stream input channel "
+ inputStreamChannel.getID());
}
inputStreamChannel = inputChannel;
inputChannel.attachIOChannel(ioChannel);
} finally {
inputMappingLock.unlock();
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(
"Stream input channel {} associates to physical channel {}.",
inputChannel,
ChannelContext.channelToString(ioChannel),
new ThreadStackDump());
}
}
/**
* Remove the link between a logical input channel and a physical IO channel. And the IO channel reading gets resumed
* anyway.
* */
final void deMapInputChannel() {
Channel channel = null;
StreamInputChannel<?> old = null;
inputMappingLock.lock();
try {
if (inputStreamChannel != null) {
old = inputStreamChannel;
old.release();
inputStreamChannel = null;
}
} finally {
inputMappingLock.unlock();
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(
"Stream input channel {} disassociated from physical channel {}.",
old,
ChannelContext.channelToString(channel),
new ThreadStackDump());
}
}
/**
* Link the logical outputChannel with the physical ioChannel.
*
* @param outputChannel the logical channel.
* */
final void mapOutputChannel(final StreamOutputChannel<?> outputChannel) {
Preconditions.checkNotNull(outputChannel);
Channel ioChannel = ownerChannelContext.getChannel();
outputMappingLock.lock();
try {
if (outputStreamChannel != null) {
throw new IllegalStateException(
"Physical channel "
+ ChannelContext.channelToString(ownerChannelContext.getChannel())
+ " already attached to stream output channel "
+ outputStreamChannel.getID());
}
outputStreamChannel = outputChannel;
outputChannel.attachIOChannel(ioChannel);
} finally {
outputMappingLock.unlock();
}
}
/**
* Remove the link between a logical output channel and a physical IO channel.
* */
final void deMapOutputChannel() {
outputMappingLock.lock();
try {
if (outputStreamChannel != null) {
outputStreamChannel.detachIOChannel();
outputStreamChannel = null;
}
} finally {
outputMappingLock.unlock();
}
}
}