private synchronized ViewCycleType waitForNextCycle() throws InterruptedException {
    while (true) {
      long currentTimeNanos = System.nanoTime();
      ViewCycleTriggerResult triggerResult = getMasterCycleTrigger().query(currentTimeNanos);

      ViewCycleEligibility cycleEligibility = triggerResult.getCycleEligibility();
      if (_forceTriggerCycle) {
        cycleEligibility = ViewCycleEligibility.FORCE;
        _forceTriggerCycle = false;
      }
      if (cycleEligibility == ViewCycleEligibility.FORCE
          || cycleEligibility == ViewCycleEligibility.ELIGIBLE && _marketDataChanged) {
        _marketDataChanged = false;
        ViewCycleType cycleType = triggerResult.getCycleType();
        if (_previousCycleReference == null) {
          // Cannot do a delta if we have no previous cycle
          cycleType = ViewCycleType.FULL;
        }
        try {
          getMasterCycleTrigger().cycleTriggered(currentTimeNanos, cycleType);
        } catch (Exception e) {
          s_logger.error("Error notifying trigger of intention to execute cycle", e);
        }
        s_logger.debug("Eligible for {} cycle", cycleType);
        return cycleType;
      }

      // Going to sleep
      long wakeUpTime = triggerResult.getNextStateChangeNanos();
      if (_marketDataChanged) {
        s_logger.debug("Sleeping until eligible to perform the next computation cycle");
        // No amount of market data can make us eligible for a computation cycle any sooner.
        _wakeOnMarketDataChanged = false;
      } else {
        s_logger.debug("Sleeping until forced to perform the next computation cycle");
        _wakeOnMarketDataChanged = cycleEligibility == ViewCycleEligibility.ELIGIBLE;
      }

      long sleepTime = wakeUpTime - currentTimeNanos;
      sleepTime = Math.max(0, sleepTime);
      sleepTime /= NANOS_PER_MILLISECOND;
      sleepTime +=
          1; // Could have been rounded down during division so ensure only woken after state change
      s_logger.debug("Waiting for {} ms", sleepTime);
      try {
        // This could wait until end of time. In this case, only marketDataChanged() or
        // triggerCycle() will wake it up
        wait(sleepTime);
      } catch (InterruptedException e) {
        // We support interruption as a signal that we have been terminated. If we're interrupted
        // without having been
        // terminated, we'll just return to this method and go back to sleep.
        Thread.interrupted();
        s_logger.info("Interrupted while delaying. Continuing operation.");
        throw e;
      }
    }
  }
  private CompiledViewDefinitionWithGraphsImpl getCompiledViewDefinition(
      Instant valuationTime, VersionCorrection versionCorrection) {
    long functionInitId =
        getProcessContext()
            .getFunctionCompilationService()
            .getFunctionCompilationContext()
            .getFunctionInitId();
    CompiledViewDefinitionWithGraphsImpl compiledViewDefinition;
    updateViewDefinitionIfRequired();
    if (_compilationDirty) {
      _compilationDirty = false;
      invalidateCachedCompiledViewDefinition();
      compiledViewDefinition = null;
    } else {
      compiledViewDefinition = getCachedCompiledViewDefinition();
    }
    if (compiledViewDefinition != null
        && compiledViewDefinition.isValidFor(valuationTime)
        && functionInitId == compiledViewDefinition.getFunctionInitId()) {
      // Existing cached model is valid (an optimisation for the common case of similar, increasing
      // valuation times)
      return compiledViewDefinition;
    }

    try {
      MarketDataAvailabilityProvider availabilityProvider =
          getMarketDataProvider().getAvailabilityProvider();
      ViewCompilationServices compilationServices =
          getProcessContext().asCompilationServices(availabilityProvider);
      compiledViewDefinition =
          ViewDefinitionCompiler.compile(
              _viewDefinition, compilationServices, valuationTime, versionCorrection);

      if (isTerminated()) {
        return compiledViewDefinition; // [PLAT-1904] If we can't terminate the compilation at least
                                       // avoid doing the subscribe etc.
      }
    } catch (Exception e) {
      String message =
          MessageFormat.format(
              "Error compiling view definition {0} for time {1}",
              getViewProcess().getDefinitionId(), valuationTime);
      viewDefinitionCompilationFailed(valuationTime, new OpenGammaRuntimeException(message, e));
      throw new OpenGammaRuntimeException(message, e);
    }
    setCachedCompiledViewDefinition(compiledViewDefinition);
    // [PLAT-984]
    // Assume that valuation times are increasing in real-time towards the expiry of the view
    // definition, so that we
    // can predict the time to expiry. If this assumption is wrong then the worst we do is trigger
    // an unnecessary
    // cycle. In the predicted case, we trigger a cycle on expiry so that any new market data
    // subscriptions are made
    // straight away.
    if (compiledViewDefinition.getValidTo() != null) {
      Duration durationToExpiry =
          getMarketDataProvider()
              .getRealTimeDuration(valuationTime, compiledViewDefinition.getValidTo());
      long expiryNanos = System.nanoTime() + durationToExpiry.toNanosLong();
      _compilationExpiryCycleTrigger.set(expiryNanos, ViewCycleTriggerResult.forceFull());
    } else {
      _compilationExpiryCycleTrigger.reset();
    }

    // Notify the view that a (re)compilation has taken place before going on to do any
    // time-consuming work.
    // This might contain enough for clients to e.g. render an empty grid in which results will
    // later appear.
    viewDefinitionCompiled(compiledViewDefinition);

    // Update the market data subscriptions to whatever is now required, ensuring the computation
    // cycle can find the
    // required input data when it is executed.
    setMarketDataSubscriptions(compiledViewDefinition.getMarketDataRequirements().keySet());
    return compiledViewDefinition;
  }