Package eu.stratosphere.runtime.io.channels

Source Code of eu.stratosphere.runtime.io.channels.InputChannel

/***********************************************************************************************************************
* Copyright (C) 2010-2013 by the Stratosphere project (http://stratosphere.eu)
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
**********************************************************************************************************************/

package eu.stratosphere.runtime.io.channels;

import eu.stratosphere.core.io.IOReadableWritable;
import eu.stratosphere.nephele.event.task.AbstractEvent;
import eu.stratosphere.nephele.event.task.AbstractTaskEvent;
import eu.stratosphere.nephele.jobgraph.JobID;
import eu.stratosphere.runtime.io.Buffer;
import eu.stratosphere.runtime.io.gates.InputChannelResult;
import eu.stratosphere.runtime.io.network.bufferprovider.BufferAvailabilityListener;
import eu.stratosphere.runtime.io.network.bufferprovider.BufferProvider;
import eu.stratosphere.runtime.io.network.Envelope;
import eu.stratosphere.runtime.io.gates.InputGate;
import eu.stratosphere.runtime.io.serialization.AdaptiveSpanningRecordDeserializer;
import eu.stratosphere.runtime.io.serialization.RecordDeserializer;
import eu.stratosphere.runtime.io.serialization.RecordDeserializer.DeserializationResult;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Queue;

/**
* InputChannel is an abstract base class to all different kinds of concrete
* input channels that can be used. Input channels are always parameterized to
* a specific type that can be transported through the channel.

* @param <T> The Type of the record that can be transported through the channel.
*/
public class InputChannel<T extends IOReadableWritable> extends Channel implements BufferProvider {

  private final InputGate<T> inputGate;

  /**
   * The log object used to report warnings and errors.
   */
  private static final Log LOG = LogFactory.getLog(InputChannel.class);

  /**
   * The deserializer used to deserialize records.
   */
  private final RecordDeserializer<T> deserializer;

  /**
   * Buffer for the uncompressed (raw) data.
   */
  private Buffer dataBuffer;

  private AbstractTaskEvent currentEvent;

  /**
   * The exception observed in this channel while processing the buffers. Checked and thrown
   * per-buffer.
   */
  private volatile IOException ioException;

  /**
   * Stores the number of bytes read through this input channel since its instantiation.
   */
  private long amountOfDataTransmitted;

  private volatile boolean brokerAggreedToCloseChannel;

  // -------------------------------------------------------------------------------------------

  private int lastReceivedEnvelope = -1;

  private ChannelID lastSourceID = null;

  private boolean destroyCalled = false;

  // ----------------------

  private Queue<Envelope> queuedEnvelopes = new ArrayDeque<Envelope>();

  private Iterator<AbstractEvent> pendingEvents;

  /**
   * Constructs an input channel with a given input gate associated.
   *
   * @param inputGate
   *        the input gate this channel is connected to
   * @param channelIndex
   *        the index of the channel in the input gate
   * @param channelID
   *        the ID of the channel
   * @param connectedChannelID
   *        the ID of the channel this channel is connected to
   */
  public InputChannel(final InputGate<T> inputGate, final int channelIndex, final ChannelID channelID,
            final ChannelID connectedChannelID, ChannelType type) {
    super(channelIndex, channelID, connectedChannelID, type);
    this.inputGate = inputGate;
    this.deserializer = new AdaptiveSpanningRecordDeserializer<T>();
  }

  /**
   * Returns the input gate associated with the input channel.
   *
   * @return the input gate associated with the input channel.
   */
  public InputGate<T> getInputGate() {
    return this.inputGate;
  }

  /**
   * Reads a record from the input channel. If currently no record is available the method
   * returns <code>null</code>. If the channel is closed (i.e. no more records will be received), the method
   * throws an {@link EOFException}.
   *
   * @return a record that has been transported through the channel or <code>null</code> if currently no record is
   *         available
   * @throws IOException
   *         thrown if the input channel is already closed {@link EOFException} or a transmission error has occurred
   */
//  public abstract InputChannelResult readRecord(T target) throws IOException;

  /**
   * Immediately closes the input channel. The corresponding output channels are
   * notified if necessary. Any remaining records in any buffers or queue is considered
   * irrelevant and is discarded.
   *
   * @throws InterruptedException
   *         thrown if the thread is interrupted while waiting for the channel to close
   * @throws IOException
   *         thrown if an I/O error occurs while closing the channel
   */
//  public abstract void close() throws IOException, InterruptedException;



  @Override
  public boolean isInputChannel() {
    return true;
  }


  @Override
  public JobID getJobID() {
    return this.inputGate.getJobID();
  }

//  public abstract AbstractTaskEvent getCurrentEvent();

  private DeserializationResult lastDeserializationResult;


  public InputChannelResult readRecord(T target) throws IOException {
    if (this.dataBuffer == null) {
      if (isClosed()) {
        return InputChannelResult.END_OF_STREAM;
      }

      // get the next element we need to handle (buffer or event)
      BufferOrEvent boe = getNextBufferOrEvent();

      if (boe == null) {
        throw new IllegalStateException("Input channel was queries for data even though none was announced available.");
      }

      // handle events
      if (boe.isEvent())
      {
        // sanity check: an event may only come after a complete record.
        if (this.deserializer.hasUnfinishedData()) {
          throw new IllegalStateException("Channel received an event before completing the current partial record.");
        }

        AbstractEvent evt = boe.getEvent();
        if (evt.getClass() == ChannelCloseEvent.class) {
          this.brokerAggreedToCloseChannel = true;
          return InputChannelResult.END_OF_STREAM;
        }
        else if (evt.getClass() == EndOfSuperstepEvent.class) {
          return InputChannelResult.END_OF_SUPERSTEP;
        }
        else if (evt instanceof AbstractTaskEvent) {
          this.currentEvent = (AbstractTaskEvent) evt;
          return InputChannelResult.TASK_EVENT;
        }
        else {
          LOG.error("Received unknown event: " + evt);
          return InputChannelResult.NONE;
        }
      } else {
        // buffer case
        this.dataBuffer = boe.getBuffer();
        this.deserializer.setNextMemorySegment(this.dataBuffer.getMemorySegment(), this.dataBuffer.size());
      }
    }

    DeserializationResult deserializationResult = this.deserializer.getNextRecord(target);
    this.lastDeserializationResult = deserializationResult;

    if (deserializationResult.isBufferConsumed()) {
      releasedConsumedReadBuffer(this.dataBuffer);
      this.dataBuffer = null;
    }

    if (deserializationResult == DeserializationResult.INTERMEDIATE_RECORD_FROM_BUFFER) {
      return InputChannelResult.INTERMEDIATE_RECORD_FROM_BUFFER;
    } else if (deserializationResult == DeserializationResult.LAST_RECORD_FROM_BUFFER) {
      return InputChannelResult.LAST_RECORD_FROM_BUFFER;
    } else if (deserializationResult == DeserializationResult.PARTIAL_RECORD) {
      return InputChannelResult.NONE;
    } else {
      throw new IllegalStateException();
    }
  }

  @Override
  public ChannelType getChannelType() {
    return null;
  }

  @Override
  public boolean isClosed() throws IOException{
    if (this.ioException != null) {
      throw new IOException("An error occurred in the channel: " + this.ioException.getMessage(), this.ioException);
    } else {
      return this.brokerAggreedToCloseChannel;
    }
  }

  public void close() throws IOException, InterruptedException {

    this.deserializer.clear();
    if (this.dataBuffer != null) {
      releasedConsumedReadBuffer(this.dataBuffer);
      this.dataBuffer = null;
    }

    // This code fragment makes sure the isClosed method works in case the channel input has not been fully consumed
    while (!this.brokerAggreedToCloseChannel)
    {
      BufferOrEvent next = getNextBufferOrEvent();
      if (next != null) {
        if (next.isEvent()) {
          if (next.getEvent() instanceof ChannelCloseEvent) {
            this.brokerAggreedToCloseChannel = true;
          }
        } else {
          releasedConsumedReadBuffer(next.getBuffer());
        }
      } else {
        Thread.sleep(200);
      }
    }

    // Send close event to indicate the input channel has successfully
    // processed all data it is interested in.
    transferEventToOutputChannel(new ChannelCloseEvent());
  }


  private void releasedConsumedReadBuffer(Buffer buffer) {
    this.amountOfDataTransmitted += buffer.size();
    buffer.recycleBuffer();
  }


  public void notifyGateThatInputIsAvailable() {
    this.getInputGate().notifyRecordIsAvailable(getIndex());
  }


  @Override
  public void transferEvent(AbstractEvent event) throws IOException, InterruptedException {
    transferEventToOutputChannel(event);
  }


  public void reportIOException(IOException ioe) {
    this.ioException = ioe;
  }


  @Override
  public void releaseAllResources() {
    this.brokerAggreedToCloseChannel = true;
    this.deserializer.clear();

    // The buffers are recycled by the input channel wrapper
  }

  /**
   * Notify the channel that a data unit has been consumed.
   */
  public void notifyDataUnitConsumed() {
    this.getInputGate().notifyDataUnitConsumed(getIndex());
  }

  public AbstractTaskEvent getCurrentEvent() {
    AbstractTaskEvent e = this.currentEvent;
    this.currentEvent = null;
    return e;
  }

  // InputChannelContext

  @Override
  public void queueEnvelope(Envelope envelope) {
    // The sequence number of the envelope to be queued
    final int sequenceNumber = envelope.getSequenceNumber();

    synchronized (this.queuedEnvelopes) {

      if (this.destroyCalled) {
        final Buffer buffer = envelope.getBuffer();
        if (buffer != null) {
          buffer.recycleBuffer();
        }
        return;
      }

      final int expectedSequenceNumber = this.lastReceivedEnvelope + 1;
      if (sequenceNumber != expectedSequenceNumber) {
        // This is a problem, now we are actually missing some data
        reportIOException(new IOException("Expected data packet " + expectedSequenceNumber + " but received " + sequenceNumber));

        // notify that something (an exception) is available
        notifyGateThatInputIsAvailable();

        if (LOG.isDebugEnabled()) {
          LOG.debug("Input channel " + this.toString() + " expected envelope " + expectedSequenceNumber
              + " but received " + sequenceNumber);
        }

        // rescue the buffer
        final Buffer buffer = envelope.getBuffer();
        if (buffer != null) {
          buffer.recycleBuffer();
        }
      } else {

        this.queuedEnvelopes.add(envelope);
        this.lastReceivedEnvelope = sequenceNumber;
        this.lastSourceID = envelope.getSource();

        // Notify the channel about the new data. notify as much as there is (buffer plus once per event)
        if (envelope.getBuffer() != null) {
          notifyGateThatInputIsAvailable();
        }

        List<? extends AbstractEvent> events = envelope.deserializeEvents();

        if (events != null) {
          for (int i = 0; i < events.size(); i++) {
            notifyGateThatInputIsAvailable();
          }
        }
      }
    }
  }

  @Override
  public void destroy() {
    final Queue<Buffer> buffersToRecycle = new ArrayDeque<Buffer>();

    synchronized (this.queuedEnvelopes) {
      this.destroyCalled = true;

      while (!this.queuedEnvelopes.isEmpty()) {
        final Envelope envelope = this.queuedEnvelopes.poll();
        if (envelope.getBuffer() != null) {
          buffersToRecycle.add(envelope.getBuffer());
        }
      }
    }

    while (!buffersToRecycle.isEmpty()) {
      buffersToRecycle.poll().recycleBuffer();
    }
  }

  public void logQueuedEnvelopes() {
    int numberOfQueuedEnvelopes = 0;
    int numberOfQueuedEnvelopesWithMemoryBuffers = 0;
    int numberOfQueuedEnvelopesWithFileBuffers = 0;

    synchronized (this.queuedEnvelopes) {

      final Iterator<Envelope> it = this.queuedEnvelopes.iterator();
      while (it.hasNext()) {

        final Envelope envelope = it.next();
        ++numberOfQueuedEnvelopes;
        final Buffer buffer = envelope.getBuffer();
        if (buffer == null) {
          continue;
        }

        ++numberOfQueuedEnvelopesWithMemoryBuffers;
      }
    }

    System.out.println("\t\t" + this.toString() + ": " + numberOfQueuedEnvelopes + " ("
        + numberOfQueuedEnvelopesWithMemoryBuffers + ", " + numberOfQueuedEnvelopesWithFileBuffers + ")");

  }

  @Override
  public Buffer requestBuffer(int minBufferSize) throws IOException {
    return this.inputGate.requestBuffer(minBufferSize);
  }

  @Override
  public Buffer requestBufferBlocking(int minBufferSize) throws IOException, InterruptedException {
    return this.inputGate.requestBufferBlocking(minBufferSize);
  }

  @Override
  public int getBufferSize() {
    return this.inputGate.getBufferSize();
  }

  @Override
  public void reportAsynchronousEvent() {
    this.inputGate.reportAsynchronousEvent();
  }

  @Override
  public BufferAvailabilityRegistration registerBufferAvailabilityListener(BufferAvailabilityListener listener) {
    return this.inputGate.registerBufferAvailabilityListener(listener);
  }

  // ChannelBroker

  public BufferOrEvent getNextBufferOrEvent() throws IOException {
    // return pending events first
    if (this.pendingEvents != null) {
      // if the field is not null, it must always have a next value!
      BufferOrEvent next = new BufferOrEvent(this.pendingEvents.next());
      if (!this.pendingEvents.hasNext()) {
        this.pendingEvents = null;
      }
      return next;
    }

    // if no events are pending, get the next buffer
    Envelope nextEnvelope;
    synchronized (this.queuedEnvelopes) {
      if (this.queuedEnvelopes.isEmpty()) {
        return null;
      }
      nextEnvelope = this.queuedEnvelopes.poll();
    }

    // schedule events as pending, because events come always after the buffer!
    List<AbstractEvent> events = (List<AbstractEvent>) nextEnvelope.deserializeEvents();
    Iterator<AbstractEvent> eventsIt = events.iterator();
    if (eventsIt.hasNext()) {
      this.pendingEvents = eventsIt;
    }

    // get the buffer, if there is one
    if (nextEnvelope.getBuffer() != null) {
      return new BufferOrEvent(nextEnvelope.getBuffer());
    }
    else if (this.pendingEvents != null) {
      // if the field is not null, it must always have a next value!
      BufferOrEvent next = new BufferOrEvent(this.pendingEvents.next());
      if (!this.pendingEvents.hasNext()) {
        this.pendingEvents = null;
      }

      return next;
    }
    else {
      // no buffer and no events, this should be an error
      throw new IOException("Received an envelope with neither data nor events.");
    }
  }

  public void transferEventToOutputChannel(AbstractEvent event) throws IOException, InterruptedException {
    Envelope ephemeralEnvelope = new Envelope(0, getJobID(), getID());
    ephemeralEnvelope.serializeEventList(Arrays.asList(event));

    this.envelopeDispatcher.dispatchFromInputChannel(ephemeralEnvelope);
  }
}
TOP

Related Classes of eu.stratosphere.runtime.io.channels.InputChannel

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.