eu.stratosphere.nephele.executiongraph.ExecutionGraphIterator Maven / Gradle / Ivy
/***********************************************************************************************************************
* 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.nephele.executiongraph;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Stack;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* This class provides an implementation of the {@link Iterator} interface which allows to
* traverse an execution graph and visit every reachable vertex exactly once. The order
* in which the vertices are visited corresponds to the order of their discovery in a depth first
* search.
*
* This class is not thread-safe.
*
*/
public class ExecutionGraphIterator implements Iterator {
/**
* The log object used for debugging.
*/
private static final Log LOG = LogFactory.getLog(ExecutionGraphIterator.class);
/**
* The execution this iterator traverses.
*/
private final ExecutionGraph executionGraph;
/**
* Stores whether the graph is traversed starting from the input or the output vertices.
*/
private final boolean forward;
/**
* The stage that should be traversed by this iterator.
*/
private final int startStage;
/**
* Stores whether the iterator is confined to the start stage or not.
*/
private final boolean confinedToStage;
/**
* The number of visited vertices from the entry set (either input or output vertices).
*/
private int numVisitedEntryVertices = 0;
/**
* Stack used for the depth first search.
*/
private final Stack traversalStack = new Stack();
/**
* Set of already visited vertices during traversal.
*/
private final Set alreadyVisited = new HashSet();
/**
* Auxiliary class which stores which vertices have already been visited.
*
*/
private static class TraversalEntry {
/**
* Execution vertex this entry has been created for.
*/
private final ExecutionVertex executionVertex;
/**
* Next gate to traverse.
*/
private int currentGate;
/**
* Next channel to traverse.
*/
private int currentChannel;
/**
* Constructs a new traversal entry.
*
* @param executionVertex
* the execution vertex this entry belongs to
* @param currentGate
* the gate index to use to visit the next vertex
* @param currentChannel
* the channel index to use to visit the next vertex
*/
public TraversalEntry(final ExecutionVertex executionVertex, final int currentGate, final int currentChannel) {
this.executionVertex = executionVertex;
this.currentGate = currentGate;
this.currentChannel = currentChannel;
}
/**
* Returns the execution vertex this entry belongs to.
*
* @return the execution vertex this entry belongs to
*/
public ExecutionVertex getExecutionVertex() {
return this.executionVertex;
}
/**
* Returns the gate index to use to visit the next vertex.
*
* @return the gate index to use to visit the next vertex
*/
public int getCurrentGate() {
return this.currentGate;
}
/**
* Returns the channel index to use to visit the next vertex.
*
* @return the channel index to use to visit the next vertex
*/
public int getCurrentChannel() {
return this.currentChannel;
}
/**
* Increases the channel index by one.
*/
public void increaseCurrentChannel() {
this.currentChannel++;
}
/**
* Increases the gate index by one.
*/
public void increaseCurrentGate() {
this.currentGate++;
}
/**
* Resets the channel index.
*/
public void resetCurrentChannel() {
this.currentChannel = 0;
}
}
/**
* Creates a new execution graph iterator.
*
* @param executionGraph
* the execution graph that should be traversed
* @param forward
* true
if the graph should be traversed in correct order, false
to traverse it in
* reverse order
*/
public ExecutionGraphIterator(final ExecutionGraph executionGraph, final boolean forward) {
this(executionGraph, forward ? 0 : (executionGraph.getNumberOfStages() - 1), false, forward);
}
/**
* Creates a new execution graph iterator.
*
* @param executionGraph
* the execution graph that should be traversed
* @param startStage
* the index of the stage of the graph where the traversal is supposed to begin
* @param confinedToStage
* false
if the graph iterator is allowed to traverse to upper (in case of reverse order
* traversal lower) stages, true
otherwise.
* @param forward
* true
if the graph should be traversed in correct order, false
to traverse it in
* reverse order
*/
public ExecutionGraphIterator(final ExecutionGraph executionGraph, final int startStage,
final boolean confinedToStage, final boolean forward) {
this.executionGraph = executionGraph;
this.forward = forward;
this.startStage = startStage;
this.confinedToStage = confinedToStage;
if (startStage >= this.executionGraph.getNumberOfStages()) {
return;
}
if (forward) {
if (executionGraph.getNumberOfInputVertices(startStage) > 0) {
final TraversalEntry te = new TraversalEntry(executionGraph.getInputVertex(startStage, 0), 0, 0);
this.traversalStack.push(te);
this.alreadyVisited.add(te.getExecutionVertex());
}
} else {
if (executionGraph.getNumberOfOutputVertices(startStage) > 0) {
final TraversalEntry te = new TraversalEntry(executionGraph.getOutputVertex(startStage, 0), 0, 0);
this.traversalStack.push(te);
this.alreadyVisited.add(te.getExecutionVertex());
}
}
}
/**
* Creates a new execution graph iterator. This constructor can be used to
* traverse only specific parts of the graph starting at startVertex
.
* The iterator will not switch to the next input/output vertex of an output/input vertex
* has been reached.
*
* @param executionGraph
* the execution graph that should be traversed
* @param startVertex
* the vertex to start the traversal from
* @param forward
* true
if the graph should be traversed in correct order, false
to reverse it in
* reverse order
*/
public ExecutionGraphIterator(final ExecutionGraph executionGraph, final ExecutionVertex startVertex,
final boolean forward) {
this.executionGraph = executionGraph;
this.forward = forward;
this.numVisitedEntryVertices = -1;
this.startStage = 0;
this.confinedToStage = false;
final TraversalEntry te = new TraversalEntry(startVertex, 0, 0);
this.traversalStack.push(te);
this.alreadyVisited.add(startVertex);
}
@Override
public boolean hasNext() {
if (this.traversalStack.isEmpty()) {
if (this.numVisitedEntryVertices < 0) {
// User chose a specific starting vertex
return false;
}
++this.numVisitedEntryVertices;
if (this.forward) {
if (this.executionGraph.getNumberOfInputVertices(this.startStage) <= this.numVisitedEntryVertices) {
return false;
}
} else {
if (this.executionGraph.getNumberOfOutputVertices(this.startStage) <= this.numVisitedEntryVertices) {
return false;
}
}
}
return true;
}
@Override
public ExecutionVertex next() {
if (this.traversalStack.isEmpty()) {
if (this.numVisitedEntryVertices < 0) {
// User chose a specific entry vertex
return null;
}
TraversalEntry newentry;
if (this.forward) {
newentry = new TraversalEntry(this.executionGraph.getInputVertex(this.startStage,
this.numVisitedEntryVertices),
0, 0);
} else {
newentry = new TraversalEntry(this.executionGraph.getOutputVertex(this.startStage,
this.numVisitedEntryVertices),
0, 0);
}
this.traversalStack.push(newentry);
this.alreadyVisited.add(newentry.getExecutionVertex());
}
final ExecutionVertex returnVertex = this.traversalStack.peek().getExecutionVertex();
// Propose vertex to be visited next
do {
final TraversalEntry te = this.traversalStack.peek();
// Check if we can traverse deeper into the graph
final ExecutionVertex candidateVertex = getCandidateVertex(te, forward);
if (candidateVertex == null) {
// Pop it from the stack
this.traversalStack.pop();
} else {
// Create new entry and put it on the stack
final TraversalEntry newte = new TraversalEntry(candidateVertex, 0, 0);
this.traversalStack.push(newte);
this.alreadyVisited.add(candidateVertex);
break;
}
} while (!this.traversalStack.isEmpty());
return returnVertex;
}
/**
* Returns a candidate vertex which could potentially be visited next because it is reachable from the
* currently considered vertex.
*
* @param te
* the traversal entry for the current source vertex
* @param forward
* true
if the graph should be traversed in correct order, false
to traverse it in
* reverse order
* @return a candidate vertex which could potentially be visited next
*/
private ExecutionVertex getCandidateVertex(final TraversalEntry te, final boolean forward) {
if (forward) {
while (true) {
if (this.confinedToStage && te.getCurrentChannel() == 0) {
while (currentGateLeadsToOtherStage(te, this.forward)) {
te.increaseCurrentGate();
}
}
// No more outgoing edges to consider
if (te.getCurrentGate() >= te.getExecutionVertex().getNumberOfOutputGates()) {
break;
}
if (te.getCurrentChannel() >= te.getExecutionVertex().getOutputGate(te.getCurrentGate())
.getNumberOfEdges()) {
te.increaseCurrentGate();
te.resetCurrentChannel();
} else {
final ExecutionEdge outputChannel = te.getExecutionVertex().getOutputGate(te.getCurrentGate())
.getEdge(te.getCurrentChannel());
final ExecutionVertex tmp = outputChannel.getInputGate().getVertex();
if (tmp == null) {
LOG.error("Inconsistency in vertex map found (forward)!");
}
te.increaseCurrentChannel();
if (!this.alreadyVisited.contains(tmp)) {
return tmp;
}
}
}
} else {
while (true) {
// No more outgoing edges to consider
if (te.getCurrentGate() >= te.getExecutionVertex().getNumberOfInputGates()) {
break;
}
if (te.getCurrentChannel() >= te.getExecutionVertex().getInputGate(te.getCurrentGate())
.getNumberOfEdges()) {
te.increaseCurrentGate();
te.resetCurrentChannel();
} else {
final ExecutionEdge inputChannel = te.getExecutionVertex().getInputGate(te.getCurrentGate()).getEdge(te.getCurrentChannel());
final ExecutionVertex tmp = inputChannel.getOutputGate().getVertex();
if (tmp == null) {
LOG.error("Inconsistency in vertex map found (backward)!");
}
te.increaseCurrentChannel();
if (!this.alreadyVisited.contains(tmp)) {
return tmp;
}
}
}
}
return null;
}
private boolean currentGateLeadsToOtherStage(final TraversalEntry te, final boolean forward) {
final ExecutionGroupVertex groupVertex = te.getExecutionVertex().getGroupVertex();
if (forward) {
if (te.getCurrentGate() >= groupVertex.getNumberOfForwardLinks()) {
return false;
}
final ExecutionGroupEdge edge = groupVertex.getForwardEdge(te.getCurrentGate());
if (edge.getTargetVertex().getStageNumber() == groupVertex.getStageNumber()) {
return false;
}
} else {
if (te.getCurrentGate() >= groupVertex.getNumberOfBackwardLinks()) {
return false;
}
final ExecutionGroupEdge edge = groupVertex.getBackwardEdge(te.getCurrentGate());
if (edge.getSourceVertex().getStageNumber() == groupVertex.getStageNumber()) {
return false;
}
}
return true;
}
@Override
public void remove() {
throw new UnsupportedOperationException("The method remove is not implemented for this type of iterator");
}
}