/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.engine.depgraph; import java.util.Collection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.opengamma.engine.function.exclusion.FunctionExclusionGroup; import com.opengamma.engine.value.ValueRequirement; /** * Resolves an individual requirement by aggregating the results of any existing tasks already resolving that requirement. If these missed an exploration because of a recursion constraint introduced * by their parent tasks, a "fallback" task is created to finish the job for the caller's parent. */ /* package */final class RequirementResolver extends AggregateResolvedValueProducer { private static final Logger s_logger = LoggerFactory.getLogger(RequirementResolver.class); private final ResolveTask _parentTask; private final Collection<FunctionExclusionGroup> _functionExclusion; // _tasks is just used to spot the recursionDetected flag - not ref-Counted private ResolveTask[] _tasks; private ResolveTask _fallback; private ResolvedValue[] _coreResults; public RequirementResolver(final ValueRequirement valueRequirement, final ResolveTask parentTask, final Collection<FunctionExclusionGroup> functionExclusion) { super(valueRequirement); s_logger.debug("Created requirement resolver {}/{}", valueRequirement, parentTask); _parentTask = parentTask; _functionExclusion = functionExclusion; } public void setTasks(final GraphBuildingContext context, final ResolveTask[] tasks) { _tasks = tasks; for (ResolveTask task : tasks) { addProducer(context, task); } } @Override protected boolean isLastResult() { // Caller already holds monitor - see javadoc if (_fallback == null) { for (ResolveTask task : _tasks) { if (!task.wasRecursionDetected()) { // One ran to completion without hitting a recursion constraint so no fallback task will be created - this is the last result return true; } } // A fallback task may be created during the finished call, so suppress the last result indicator for now return false; } else { // The fallback task is active so let its last result carry through return true; } } @Override protected void finished(final GraphBuildingContext context) { boolean useFallback = false; ResolveTask fallback; synchronized (this) { final int pendingTasks = getPendingTasks(); if (pendingTasks == Integer.MIN_VALUE) { // We've already been discarded (everything was released when we went to rc=0) s_logger.debug("Ignoring finish on discarded {}", this); return; } assert (pendingTasks == 0) || (pendingTasks == 1); // Either the final pending task running with the "lastValue" flag, or all tasks have finished fallback = _fallback; if (fallback == null) { // Only create a fallback if none of the others ran to completion without hitting a recursion constraint. useFallback = true; for (ResolveTask task : _tasks) { if (!task.wasRecursionDetected()) { useFallback = false; break; } } _tasks = null; } else { // local variable takes open reference from _fallback _fallback = null; } } if ((fallback == null) && useFallback) { fallback = context.getOrCreateTaskResolving(getValueRequirement(), _parentTask, _functionExclusion); s_logger.debug("Creating fallback task {}", fallback); synchronized (this) { assert _fallback == null; // _fallback takes the open reference from the local variable _fallback = fallback; _coreResults = getResults(); } addProducer(context, fallback); return; } super.finished(context); if (fallback != null) { // Keep any fallback tasks that are recursion free - to prevent future fallbacks for this requirement if (fallback.wasRecursionDetected()) { final ResolvedValue[] fallbackResults = getResults(); // If this resolver was ref-counted to zero (nothing subscribed to it) then the results can be null at this point if ((fallbackResults == null) || (fallbackResults.length == 0)) { // Task produced no new results - discard s_logger.debug("Discarding fallback task {} by {}", fallback, this); context.discardTask(fallback); } else { boolean matched = true; synchronized (this) { for (int i = 0; i < fallbackResults.length; i++) { boolean found = false; for (int j = 0; j < _coreResults.length; j++) { if (fallbackResults[i].equals(_coreResults[j])) { found = true; break; } } if (!found) { matched = false; break; } } } if (matched) { // Task didn't produce any new results - discard context.discardTask(fallback); } } } else { s_logger.debug("Keeping fallback task {} by {}", fallback, this); } fallback.release(context); } } @Override public String toString() { return "Resolve" + getObjectId() + "[" + getValueRequirement() + ", " + _parentTask + "]"; } @Override public int release(final GraphBuildingContext context) { final int count = super.release(context); if (count == 0) { final ResolveTask fallback; synchronized (this) { fallback = _fallback; _fallback = null; _tasks = null; } if (fallback != null) { // Release/discard the fallback task fallback.release(context); } } return count; } }