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; }