@Override
 protected void postRunCycle() {
   if (_previousCycleReference != null) {
     _previousCycleReference.release();
   }
   unsubscribeFromViewDefinition();
   removeMarketDataProvider();
   invalidateCachedCompiledViewDefinition();
 }
  private void executeViewCycle(
      ViewCycleType cycleType,
      EngineResourceReference<SingleComputationCycle> cycleReference,
      MarketDataSnapshot marketDataSnapshot,
      ExecutorService calcJobResultExecutorService)
      throws Exception {
    SingleComputationCycle deltaCycle;
    if (cycleType == ViewCycleType.FULL) {
      s_logger.info("Performing full computation");
      deltaCycle = null;
    } else {
      s_logger.info("Performing delta computation");
      deltaCycle = _previousCycleReference.get();
    }

    try {
      cycleReference.get().execute(deltaCycle, marketDataSnapshot, calcJobResultExecutorService);
    } catch (InterruptedException e) {
      Thread.interrupted();
      // In reality this means that the job has been terminated, and it will end as soon as we
      // return from this method.
      // In case the thread has been interrupted without terminating the job, we tidy everything up
      // as if the
      // interrupted cycle never happened so that deltas will be calculated from the previous cycle.
      s_logger.info(
          "Interrupted while executing a computation cycle. No results will be output from this cycle.");
      throw e;
    } catch (Exception e) {
      s_logger.error("Error while executing view cycle", e);
      throw e;
    }

    long durationNanos = cycleReference.get().getDuration().toNanosLong();
    _totalTimeNanos += durationNanos;
    _cycleCount += 1;
    s_logger.info(
        "Last latency was {} ms, Average latency is {} ms",
        durationNanos / NANOS_PER_MILLISECOND,
        (_totalTimeNanos / _cycleCount) / NANOS_PER_MILLISECOND);
  }
  /**
   * Determines whether to run, and runs if required, a single computation cycle using the following
   * rules:
   *
   * <ul>
   *   <li>A computation cycle can only be triggered if the relevant minimum computation period has
   *       passed since the start of the previous cycle.
   *   <li>A computation cycle will be forced if the relevant maximum computation period has passed
   *       since the start of the previous cycle.
   *   <li>A full computation is preferred over a delta computation if both are possible.
   *   <li>Performing a full computation also updates the times to the next delta computation; i.e.
   *       a full computation is considered to be as good as a delta.
   * </ul>
   */
  @Override
  protected void runOneCycle() {
    // Exception handling is important here to ensure that computation jobs do not just die quietly
    // while consumers are
    // potentially blocked, waiting for results.

    ViewCycleType cycleType;
    try {
      cycleType = waitForNextCycle();
    } catch (InterruptedException e) {
      return;
    }

    ViewCycleExecutionOptions executionOptions = null;
    try {
      if (!getExecutionOptions().getExecutionSequence().isEmpty()) {
        executionOptions =
            getExecutionOptions()
                .getExecutionSequence()
                .getNext(getExecutionOptions().getDefaultExecutionOptions());
        s_logger.debug("Next cycle execution options: {}", executionOptions);
      }
      if (executionOptions == null) {
        s_logger.info("No more view cycle execution options");
        processCompleted();
        return;
      }
    } catch (Exception e) {
      s_logger.error(
          "Error obtaining next view cycle execution options from sequence for view process "
              + getViewProcess(),
          e);
      return;
    }

    if (executionOptions.getMarketDataSpecification() == null) {
      s_logger.error("No market data specification for cycle");
      cycleExecutionFailed(
          executionOptions,
          new OpenGammaRuntimeException("No market data specification for cycle"));
      return;
    }

    MarketDataSnapshot marketDataSnapshot;
    try {
      if (getMarketDataProvider() == null
          || !getMarketDataProvider().isCompatible(executionOptions.getMarketDataSpecification())) {
        // A different market data provider is required. We support this because we can, but
        // changing provider is not the
        // most efficient operation.
        if (getMarketDataProvider() != null) {
          s_logger.info("Replacing market data provider between cycles");
        }
        replaceMarketDataProvider(executionOptions.getMarketDataSpecification());
      }

      // Obtain the snapshot in case it is needed, but don't explicitly initialise it until the data
      // is required
      marketDataSnapshot =
          getMarketDataProvider().snapshot(executionOptions.getMarketDataSpecification());
    } catch (Exception e) {
      s_logger.error("Error with market data provider", e);
      cycleExecutionFailed(
          executionOptions, new OpenGammaRuntimeException("Error with market data provider", e));
      return;
    }

    Instant compilationValuationTime;
    try {
      if (executionOptions.getValuationTime() != null) {
        compilationValuationTime = executionOptions.getValuationTime();
      } else {
        // Neither the cycle-specific options nor the defaults have overridden the valuation time so
        // use the time
        // associated with the market data snapshot. To avoid initialising the snapshot perhaps
        // before the required
        // inputs are known or even subscribed to, only ask for an indication at the moment.
        compilationValuationTime = marketDataSnapshot.getSnapshotTimeIndication();
        if (compilationValuationTime == null) {
          throw new OpenGammaRuntimeException(
              "Market data snapshot "
                  + marketDataSnapshot
                  + " produced a null indication of snapshot time");
        }
      }
    } catch (Exception e) {
      s_logger.error("Error obtaining compilation valuation time", e);
      cycleExecutionFailed(
          executionOptions,
          new OpenGammaRuntimeException("Error obtaining compilation valuation time", e));
      return;
    }

    VersionCorrection versionCorrection = getResolvedVersionCorrection();
    final CompiledViewDefinitionWithGraphsImpl compiledViewDefinition;
    try {
      compiledViewDefinition =
          getCompiledViewDefinition(compilationValuationTime, versionCorrection);
      if (isTerminated()) {
        return; // [PLAT-1904]
      }
    } catch (Exception e) {
      String message =
          MessageFormat.format(
              "Error obtaining compiled view definition {0} for time {1} at version-correction {2}",
              getViewProcess().getDefinitionId(), compilationValuationTime, versionCorrection);
      s_logger.error(message);
      cycleExecutionFailed(executionOptions, new OpenGammaRuntimeException(message, e));
      return;
    }

    try {
      if (getExecutionOptions().getFlags().contains(ViewExecutionFlags.AWAIT_MARKET_DATA)) {
        marketDataSnapshot.init(
            compiledViewDefinition.getMarketDataRequirements().keySet(),
            MARKET_DATA_TIMEOUT_MILLIS,
            TimeUnit.MILLISECONDS);
      } else {
        marketDataSnapshot.init();
      }
      if (executionOptions.getValuationTime() == null) {
        executionOptions.setValuationTime(marketDataSnapshot.getSnapshotTime());
      }
    } catch (Exception e) {
      s_logger.error("Error initializing snapshot {}", marketDataSnapshot);
      cycleExecutionFailed(
          executionOptions,
          new OpenGammaRuntimeException("Error initializing snapshot" + marketDataSnapshot, e));
    }

    EngineResourceReference<SingleComputationCycle> cycleReference;
    try {
      cycleReference = createCycle(executionOptions, compiledViewDefinition, versionCorrection);
    } catch (Exception e) {
      s_logger.error("Error creating next view cycle for view process " + getViewProcess(), e);
      return;
    }

    if (_executeCycles) {
      try {
        final SingleComputationCycle singleComputationCycle = cycleReference.get();
        final Set<String> configurationNames =
            singleComputationCycle.getAllCalculationConfigurationNames();

        final HashMap<String, Collection<ComputationTarget>> configToComputationTargets =
            new HashMap<String, Collection<ComputationTarget>>();
        for (String configName : configurationNames) {
          DependencyGraph dependencyGraph =
              singleComputationCycle.getExecutableDependencyGraph(configName);
          configToComputationTargets.put(configName, dependencyGraph.getAllComputationTargets());
        }

        final HashMap<String, Map<ValueSpecification, Set<ValueRequirement>>>
            configToTerminalOutputs =
                new HashMap<String, Map<ValueSpecification, Set<ValueRequirement>>>();
        for (String configName : configurationNames) {
          DependencyGraph dependencyGraph =
              singleComputationCycle.getExecutableDependencyGraph(configName);
          configToTerminalOutputs.put(configName, dependencyGraph.getTerminalOutputs());
        }

        cycleInitiated(
            new SimpleCycleInfo(
                marketDataSnapshot.getUniqueId(),
                compiledViewDefinition.getViewDefinition().getUniqueId(),
                versionCorrection,
                executionOptions.getValuationTime(),
                configurationNames,
                configToComputationTargets,
                configToTerminalOutputs));
        executeViewCycle(
            cycleType,
            cycleReference,
            marketDataSnapshot,
            getViewProcess().getCalcJobResultExecutorService());
      } catch (InterruptedException e) {
        // Execution interrupted - don't propagate as failure
        s_logger.info("View cycle execution interrupted for view process {}", getViewProcess());
        cycleReference.release();
        return;
      } catch (Exception e) {
        // Execution failed
        s_logger.error("View cycle execution failed for view process " + getViewProcess(), e);
        cycleReference.release();
        cycleExecutionFailed(executionOptions, e);
        return;
      }
    }

    // Don't push the results through if we've been terminated, since another computation job could
    // be running already
    // and the fact that we've been terminated means the view is no longer interested in the result.
    // Just die quietly.
    if (isTerminated()) {
      cycleReference.release();
      return;
    }

    if (_executeCycles) {
      cycleCompleted(cycleReference.get());
    }

    if (getExecutionOptions().getExecutionSequence().isEmpty()) {
      processCompleted();
    }

    if (_executeCycles) {
      if (_previousCycleReference != null) {
        _previousCycleReference.release();
      }
      _previousCycleReference = cycleReference;
    }
  }