/** * {@inheritDoc} * * <p>If the current state of this actor is still {@link State#STARTING}, the message will be * queued. It will be handled in {@link #startRunningIfQueueEmpty()} when the state is changed to * {@link State#RUNNING}. * * <p>If the current state is {@link State#RUNNING}, this method finishes the asynchronous action * started for the given port in {@link #preStart()}. * * @param inPortId index of the in-port in {@link RuntimeModule#getInPorts()} */ @Override void inPortHasSignal(int inPortId) { if (state != State.REPLAY && inPortReceivedMessage.get(inPortId)) { log.warning( String.format( "Ignoring redundant message that %s received value.", module.getInPorts().get(inPortId))); } else if (!recomputedInPorts.get(inPortId)) { log.warning( String.format( "Ignoring unexpected message that %s received a value. " + "In-ports that are expected to receive a value: %s", module.getInPorts().get(inPortId), recomputedInPorts .stream() .mapToObj(module.getInPorts()::get) .map(RuntimeInPort::getSimpleName) .collect(Collectors.toList()))); } else { inPortReceivedMessage.set(inPortId); if (state != State.STARTING) { RuntimeInPort inPort = module.getInPorts().get(inPortId); transmitValueFromInPort(inPort); // Finishes the asynchronous action started in #preStart() endAsynchronousAction(inPort); } } }
/** * 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]; }
/** * 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); }
/** * Performs the initialization steps of algorithm <em>ComputeResumeState</em>. * * <p>This method sets the state of this actor to {@link State#STARTING} and starts the * asynchronous initialization action, which is completed in {@link #startRunningIfQueueEmpty()}. * * <p>This method starts one asynchronous action for every in-port in {@link #recomputedInPorts}. * These asynchronous actions are finished in {@link #inPortHasSignal(int)}. */ @Override public void preStart() { assert state == State.STARTING; assert !requestedOutPorts.isEmpty(); // Another precondition: getPortState() is IRRELEVANT for all nodes in the dependency graph. publishStartModule(); // Start an asynchronous action for each in-port that is promised to receive a value recomputedInPorts .stream() .mapToObj(module.getInPorts()::get) .forEach( inPort -> startAsynchronousAction(inPort, "waiting for %s to receive value", inPort)); // Run algorithm ComputeResumeState computeResumeState.run(); startRunningIfQueueEmpty(); }