@Test public void testSimpleGraphWithOneScope() { Dependencies deps = new Dependencies(); deps.addDependencies(A1, createMap(A2, A3)); deps.addDependencies(A2, createMap(A3)); DependencyGraph graph = deps.getDependencyGraph(TestScope.scope1); assertEquals(createSet(A1, A2, A3), graph.getAllItems()); assertEquals(createSet(A2, A3), graph.getDependencies(A1)); assertEquals(createSet(A3), graph.getDependencies(A2)); assertEquals(Collections.EMPTY_SET, graph.getDependencies(A3)); }
@Test public void testTwoScopesGraphWithDirectAndDerivedDependencies() { Dependencies deps = new Dependencies(); deps.addDependencies(A1, createMap(A2)); deps.addDependencies(B1, createMap(B3)); deps.addChild(B1, A1); deps.addChild(B2, A2); DependencyGraph graph = deps.getDependencyGraph(TestScope.scope2); assertEquals(createSet(B1, B2, B3), graph.getAllItems()); assertEquals(createSet(B2, B3), graph.getDependencies(B1)); assertEquals(Collections.EMPTY_SET, graph.getDependencies(B2)); assertEquals(Collections.EMPTY_SET, graph.getDependencies(B3)); }
private CompositeModuleInterpreterActor(Factory factory) { super( factory.interpreterProperties, factory.stagingArea.getAnnotatedExecutionTrace(), factory.moduleId); module = factory.module; stagingArea = factory.stagingArea; interpreterPropsProvider = factory.interpreterPropsProvider; recomputedInPorts = factory.recomputedInPorts; requestedOutPorts = factory.requestedOutPorts; int numSubmodules = module.getModules().size(); childExecutors = new ActorRef[numSubmodules]; outPortsRequiringValue = (BitSet) requestedOutPorts.clone(); childActorMap = new HashMap<>(numSubmodules); dependencyGraph = new DependencyGraph(module, requestedOutPorts); for (InPortNode inPortNode : dependencyGraph.inPortNodes()) { HasValue hasValue = factory.inPortsHasValueList.get(inPortNode.getElement().getInIndex()); assert hasValue != HasValue.PENDING_VALUE_CHECK; inPortNode.setHasValue(hasValue); } inPortReceivedMessage = new BitSet(module.getInPorts().size()); submoduleOutPortsReceivedMessage = new BitSet[numSubmodules]; for (RuntimeModule submodule : module.getModules()) { submoduleOutPortsReceivedMessage[submodule.getIndex()] = new BitSet(submodule.getOutPorts().size()); } computeResumeState = new ComputeResumeState(dependencyGraph, recomputedInPorts, this::asynchronousHasValueCheck); }
/** * Starts the interpretation of the current parent module if algorithm ComputeResumeState has * finished. * * <p>This method sets the state of this actor to {@link State#RUNNING} and thus also finished the * asynchronous action that was started in {@link #preStart()}. */ private void startRunningIfQueueEmpty() { assert state == State.STARTING; if (!computeResumeState.isFinished()) { return; } // Trigger in-ports initialInPortsTrigger(); state = State.RUNNING; // Start child interpreters for all submodules that have at least one in-port whose state is // READY submodules: for (List<SubmoduleInPortNode> submoduleInPortNodes : dependencyGraph.submodulesInPortNodes()) { for (SubmoduleInPortNode submoduleInPortNode : submoduleInPortNodes) { if (submoduleInPortNode.getPortState() == PortState.READY) { getChildExecutor(submoduleInPortNode.getElement().getModule()); continue submodules; } } } // Start child interpreters for all submodules that have no in-ports and whose state is READY for (SubmoduleNode submoduleNode : dependencyGraph.submoduleNodes()) { if (submoduleNode.getElement().getInPorts().isEmpty() && submoduleNode.getPortState() == PortState.READY) { getChildExecutor(submoduleNode.getElement()); } } // Trigger all out-ports whose state is READY dependencyGraph .outPortNodes() .stream() .filter(node -> node.getPortState() == PortState.READY) .forEach(node -> outportCarriesSignal(node.getElement().getOutIndex())); if (outPortsRequiringValue.isEmpty()) { state = State.ALL_OUTPUTS; } // Finish the asynchronous action started in #preStart(). checkIfNoAsynchronousActions(); }
/** * Returns whether the given connection ends in a port that is a node in the dependency graph with * state {@link PortState#RECOMPUTE}. * * <p>This method always returns false if the connection is not within the current parent module. */ private boolean isConnectionToRecomputedNode(RuntimeConnection runtimeConnection) { if (runtimeConnection.getParentModule() == module) { DependencyGraphNode portNode = dependencyGraph.targetNode(runtimeConnection.getToPort()); if (portNode.getPortState() == PortState.RECOMPUTE) { return true; } } return false; }
/** * Returns, and if necessary creates, the submodule interpreter actor for the given submodule. * * <p>A submodule interpreter actor is created at most once during the lifetime of the current * actor. If the submodule actor is created, it will be supervised by the current actor. * * <p>This method starts one asynchronous action for each out-port of the submodule that satisfies * {@link #isOutPortNeeded(RuntimeOutPort)}; these will be finished in {@link * #submoduleOutPortNoLongerNeeded}. This prevents this actor from terminating even though a child * interpreter may still send values for out-ports. * * <p>One additional asynchronous action is started for the child actor, which will be finished in * {@link #childActorTerminated(ActorRef)}. * * @param submodule submodule * @return reference to the submodule actor */ private ActorRef getChildExecutor(RuntimeModule submodule) { assert state == State.REPLAY || state == State.RUNNING || state == State.ALL_OUTPUTS; int submoduleId = submodule.getIndex(); if (childExecutors[submoduleId] == null) { List<SubmoduleInPortNode> submoduleInPortNodes = dependencyGraph.submodulesInPortNodes().get(submoduleId); ImmutableList<HasValue> submoduleInPortHasValueList = submoduleInPortNodes .stream() .map(SubmoduleInPortNode::getHasValue) .collect(ImmutableList.collector()); BitSet submoduleRecomputedInPorts = new BitSet(submodule.getInPorts().size()); submoduleInPortNodes .stream() .filter(node -> node.getPortState() == PortState.RECOMPUTE) .forEach(node -> submoduleRecomputedInPorts.set(node.getElement().getInIndex())); BitSet submoduleRequestedOutPorts = computeResumeState.getSubmodulesNeededOutPorts()[submoduleId]; submoduleRequestedOutPorts .stream() .mapToObj(id -> submodule.getOutPorts().get(id)) .forEach( outPort -> startAsynchronousAction( outPort, "waiting for value to pass through submodule out-port %s#%s", submodule.getSimpleName(), outPort.getSimpleName())); childExecutors[submoduleId] = getContext() .actorOf( interpreterPropsProvider.provideInterpreterProps( getInterpreterProperties(), stagingArea.resolveDescendant( ExecutionTrace.empty() .resolveContent() .resolveModule(submodule.getSimpleName())), submoduleId, submoduleInPortHasValueList, submoduleRecomputedInPorts, submoduleRequestedOutPorts), submodule.getSimpleName().toString()); getContext().watch(childExecutors[submoduleId]); childActorMap.put(childExecutors[submoduleId], submodule); startAsynchronousAction( childExecutors[submoduleId], "supervising interpreter for submodule %s", submodule.getSimpleName()); } return childExecutors[submoduleId]; }
private static DependencyGraph loadGraphs(int startingAt, String... mainArgs) throws Exception { String filename; if (mainArgs.length < startingAt + 1) { return loadGraph("thread_dependency_graph.ser"); } DependencyGraph result = new DependencyGraph(); for (int i = startingAt; i < mainArgs.length; i++) { filename = mainArgs[i]; DependencyGraph gr = loadGraph(filename); if (gr == null) { return null; } result.addEdges(gr.getEdges()); } return result; }
public static void main(String... args) throws Exception { if (args.length == 0) { printHelp(); return; } DependencyGraph graph; switch (args[0]) { case "print": graph = loadGraphs(1, args); System.out.println(prettyFormat(graph)); break; case "findCycle": graph = loadGraphs(1, args); List<Dependency> cycle = graph.findCycle(); if (cycle == null) { System.out.println("no deadlock found"); } else { System.out.println("deadlocked threads: \n" + cycle); } break; case "findImpasse": graph = loadGraphs(1, args); graph = graph.findLongestCallChain(); if (graph == null) { System.out.println("no long call chain could be found!"); } else { System.out.println("longest call chain: \n" + prettyFormat(graph)); } break; case "findObject": graph = loadGraphs(2, args); List<DependencyGraph> graphs = graph.findDependenciesWith(args[1]); if (graphs.isEmpty()) { System.out.println( "thread not found! Try using the print command to see all threads and locate the name of the one you're interested in?"); } else { int numGraphs = graphs.size(); int i = 0; System.out.println("findObject \"" + args[1] + "\"\n\n"); for (DependencyGraph g : graphs) { i += 1; System.out.println("graph " + i + " of " + numGraphs + ":"); System.out.println(prettyFormat(sortDependencies(g.getEdges()))); if (i < numGraphs) { System.out.println("\n\n\n"); } } } break; default: printHelp(); break; } }
/** * Triggers all in-ports that either are in state {@link PortState#READY} or that received a value * while the state of this actor was {@link State#STARTING}. */ private void initialInPortsTrigger() { assert state == State.STARTING; state = State.REPLAY; // Trigger all in-ports whose state is READY dependencyGraph .inPortNodes() .stream() .filter(node -> node.getPortState() == PortState.READY) .forEach(node -> transmitValueFromInPort(node.getElement())); // Trigger all in-ports who received a value while state was STARTING inPortReceivedMessage.stream().forEach(this::inPortHasSignal); }
@Test public void testGetGraphByParentsTwoScopesUp() { Dependencies deps = new Dependencies(); deps.addDependencies(A1, createMap(A2, A3, A4)); deps.addDependencies(A2, createMap(A3, A4)); deps.addChild(B1, A1); deps.addChild(B1, A2); deps.addChild(B2, A3); deps.addChild(B3, A4); deps.addChild(C1, B1); deps.addChild(C2, B2); deps.addChild(C3, B3); DependencyGraph graph = deps.getDependencyGraph( TestScope.scope1, new HashSet(Arrays.asList(C1, C2)), DependencyFilter.none); assertEquals(createSet(A1, A2, A3), graph.getAllItems()); assertEquals(createSet(A2, A3), graph.getDependencies(A1)); assertEquals(createSet(A3), graph.getDependencies(A2)); assertEquals(Collections.EMPTY_SET, graph.getDependencies(A3)); DependencyGraph graphFiltered = deps.getDependencyGraph( TestScope.scope1, new HashSet(Arrays.asList(C1)), DependencyFilter.itemsContributingToTheParentDependencyWeight); assertTrue(graphFiltered.getAllItems().size() == 0); graphFiltered = deps.getDependencyGraph( TestScope.scope1, new HashSet(Arrays.asList(C1, C2)), DependencyFilter.itemsContributingToTheParentDependencyWeight); assertEquals(createSet(A1, A2, A3), graphFiltered.getAllItems()); assertEquals(createSet(A3), graphFiltered.getDependencies(A1)); assertEquals(createSet(A3), graphFiltered.getDependencies(A2)); }
/** Find the all of the dependencies of a given thread. */ public DependencyGraph findDependencyGraph(ThreadReference thread) { return graph.getSubGraph(thread); }
/** * Finds the first deadlock in the list of dependencies, or null if there are no deadlocks in the * set of dependencies. * * @return a linked list of dependencies which shows the circular dependencies. The List will be * of the form Dependency(A,B), Dependency(B,C), Dependency(C, A). */ public LinkedList<Dependency> findDeadlock() { return graph.findCycle(); }
/** Add a set of dependencies to the dependency graph to be analyzed. */ public void addDependencies(Set<Dependency> dependencies) { graph.addEdges(dependencies); }
/** Format dependency graph for displaying to a user. */ public static String prettyFormat(DependencyGraph graph) { return prettyFormat(graph.getEdges()); }
@Test public void testGetEmptyGraph() { DependencyGraph graph = new Dependencies().getDependencyGraph(TestScope.scope1); assertTrue(graph.getAllItems().isEmpty()); }