/**
  * {@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();
  }