Package com.opengamma.engine.depgraph

Source Code of com.opengamma.engine.depgraph.ResolveTask$State

/**
* Copyright (C) 2011 - 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 java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.lang.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.engine.function.ParameterizedFunction;
import com.opengamma.engine.function.exclusion.FunctionExclusionGroup;
import com.opengamma.engine.target.lazy.LazyComputationTargetResolver;
import com.opengamma.engine.value.ValueRequirement;
import com.opengamma.engine.value.ValueSpecification;

/**
* Unit of task resolution. A resolve task executes to convert a {@link ValueRequirement} into a dependency node.
*/
/* package */final class ResolveTask extends DirectResolvedValueProducer implements ContextRunnable {

  private static final Logger s_logger = LoggerFactory.getLogger(ResolveTask.class);
  private static final AtomicInteger s_nextObjectId = new AtomicInteger();

  /**
   * State within a task. As the task executes, the execution is delegated to the current state object.
   */
  protected abstract static class State implements ResolvedValueProducer.Chain {

    private final int _objectId = s_nextObjectId.getAndIncrement();
    private final ResolveTask _task;

    //private final InstanceCount _instanceCount = new InstanceCount(this);

    protected State(final ResolveTask task) {
      assert task != null;
      _task = task;
    }

    protected int getObjectId() {
      return _objectId;
    }

    protected ResolveTask getTask() {
      return _task;
    }

    protected void setTaskStateFinished(final GraphBuildingContext context) {
      getTask().finished(context);
    }

    protected boolean setTaskState(final State nextState) {
      return getTask().setState(this, nextState);
    }

    protected boolean setRunnableTaskState(final State nextState, final GraphBuildingContext context) {
      final ResolveTask task = getTask();
      if (task.setState(this, nextState)) {
        context.run(task);
        return true;
      } else {
        return false;
      }
    }

    protected boolean pushResult(final GraphBuildingContext context, final ResolvedValue resolvedValue, final boolean lastResult) {
      return getTask().pushResult(context, resolvedValue, lastResult);
    }

    protected ResolvedValue createResult(final ValueSpecification valueSpecification, final ParameterizedFunction parameterizedFunction, final Set<ValueSpecification> functionInputs,
        final Set<ValueSpecification> functionOutputs) {
      return new ResolvedValue(valueSpecification, parameterizedFunction, functionInputs, functionOutputs);
    }

    protected void storeFailure(final ResolutionFailure failure) {
      getTask().storeFailure(failure);
    }

    protected ValueRequirement getValueRequirement() {
      return getTask().getValueRequirement();
    }

    // TODO: Profile how much time is spent calling getTargetSpecification. If it is a costly op, would holding the resolved specification in the task object be justified?

    protected ComputationTargetSpecification getTargetSpecification(final GraphBuildingContext context) {
      return context.resolveTargetReference(getValueRequirement().getTargetReference());
    }

    protected ComputationTarget getComputationTarget(final GraphBuildingContext context) {
      final ComputationTargetSpecification specification = getTargetSpecification(context);
      if (specification == null) {
        return null;
      }
      final ComputationTarget target = LazyComputationTargetResolver.resolve(context.getCompilationContext().getComputationTargetResolver(), specification);
      if (target == null) {
        s_logger.warn("Computation target {} not found", specification);
      }
      return target;
    }

    protected boolean run(final GraphBuildingContext context) {
      throw new UnsupportedOperationException("Not runnable state (" + toString() + ")");
    }

    protected abstract void pump(final GraphBuildingContext context);

    @Override
    public int cancelLoopMembers(final GraphBuildingContext context, final Map<Chain, Chain.LoopState> visited) {
      return cancelLoopMembersImpl(context, visited);
    }

    protected int cancelLoopMembersImpl(final GraphBuildingContext context, final Map<Chain, Chain.LoopState> visited) {
      return getTask().cancelLoopMembers(context, visited);
    }

    /**
     * Called when the parent task is discarded.
     *
     * @param context the graph building context, not null
     */
    protected void discard(final GraphBuildingContext context) {
      // No-op; only implement if there is data to discard (e.g. cancel things to free resources) for the state
    }

  }

  /**
   * Parent value requirements.
   */
  private final Set<ValueRequirement> _parentRequirements;

  /**
   * Pre-calculated hashcode.
   */
  private final int _hashCode;

  /**
   * Current state.
   */
  private volatile State _state;

  /**
   * Function mutual exclusion group hints. Functions shouldn't be considered for the same value requirement name if their group hint is already present.
   */
  private final Collection<FunctionExclusionGroup> _functionExclusion;

  public ResolveTask(final ValueRequirement valueRequirement, final ResolveTask parent, final Collection<FunctionExclusionGroup> functionExclusion) {
    super(valueRequirement);
    final int hc;
    if (parent != null) {
      if (parent.getParentValueRequirements() != null) {
        _parentRequirements = new HashSet<ValueRequirement>(parent.getParentValueRequirements());
        _parentRequirements.add(parent.getValueRequirement());
      } else {
        _parentRequirements = Collections.singleton(parent.getValueRequirement());
      }
      hc = valueRequirement.hashCode() * 31 + _parentRequirements.hashCode();
    } else {
      _parentRequirements = null;
      hc = valueRequirement.hashCode();
    }
    if (functionExclusion != null) {
      _functionExclusion = functionExclusion;
      _hashCode = hc * 31 + functionExclusion.hashCode();
    } else {
      _functionExclusion = null;
      _hashCode = hc;
    }
    _state = new GetFunctionsStep(this);
  }

  private State getState() {
    return _state;
  }

  private synchronized boolean setState(final State previousState, final State nextState) {
    assert nextState != null;
    if (_state == previousState) {
      s_logger.debug("State transition {} to {}", previousState, nextState);
      _state = nextState;
      return true;
    } else {
      System.err.println("Invalid state transition - was " + _state + ", not " + previousState + " - not advancing to " + nextState);
      return false;
    }
  }

  @Override
  public boolean isFinished() {
    return _state == null;
  }

  @Override
  protected void finished(final GraphBuildingContext context) {
    assert _state != null;
    _state = null;
    super.finished(context);
  }

  @Override
  public boolean tryRun(final GraphBuildingContext context) {
    final State state = getState();
    assert state != null;
    if (state.run(context)) {
      // Release the lock that the context added before we got queued (or run in-line)
      release(context);
      return true;
    } else {
      return false;
    }
  }

  private Set<ValueRequirement> getParentValueRequirements() {
    return _parentRequirements;
  }

  public boolean hasParent(final ResolveTask task) {
    if (task == this) {
      return true;
    } else {
      return hasParent(task.getValueRequirement());
    }
  }

  public boolean hasParent(final ValueRequirement valueRequirement) {
    if (valueRequirement.equals(getValueRequirement())) {
      return true;
    } else if (getParentValueRequirements() == null) {
      return false;
    } else {
      return getParentValueRequirements().contains(valueRequirement);
    }
  }

  /**
   * Tests if the parent value requirements of this task are the same as a task would have if it used the given task as its parent.
   * <p>
   * This is part of a cheaper test for an existing task than creating a new instance and using the {@link #equals} method.
   *
   * @param parent the candidate parent to test, not null
   * @return true if the parent value requirements would match
   */
  public boolean hasParentValueRequirements(final ResolveTask parent) {
    if (getParentValueRequirements() != null) {
      if (parent != null) {
        if (parent.getParentValueRequirements() != null) {
          if (getParentValueRequirements().size() == parent.getParentValueRequirements().size() + 1) {
            return getParentValueRequirements().contains(parent.getValueRequirement()) && getParentValueRequirements().containsAll(parent.getParentValueRequirements());
          } else {
            return false;
          }
        } else {
          if (getParentValueRequirements().size() == 1) {
            return getParentValueRequirements().contains(parent.getValueRequirement());
          } else {
            return false;
          }
        }
      } else {
        return false;
      }
    } else {
      return parent == null;
    }
  }

  public Collection<FunctionExclusionGroup> getFunctionExclusion() {
    return _functionExclusion;
  }

  // TODO: could use a ResolveTaskKey instead of the unusual behavior of hash/equal here

  // HashCode and Equality are to allow tasks to be considered equal iff they
  // are for the same value requirement, correspond to the same resolution
  // depth (i.e. the sets of parents are equal), and have the same function
  // exclusion set

  @Override
  public int hashCode() {
    return _hashCode;
  }

  @Override
  public boolean equals(final Object o) {
    if (o == this) {
      return true;
    }
    if (!(o instanceof ResolveTask)) {
      return false;
    }
    final ResolveTask other = (ResolveTask) o;
    if (!getValueRequirement().equals(other.getValueRequirement())) {
      return false;
    }
    return ObjectUtils.equals(getParentValueRequirements(), other.getParentValueRequirements()) && ObjectUtils.equals(getFunctionExclusion(), other.getFunctionExclusion());
  }

  @Override
  protected void pumpImpl(final GraphBuildingContext context) {
    s_logger.debug("Pump called on {}", this);
    final State state = getState();
    if (state != null) {
      state.pump(context);
    }
  }

  @Override
  public String toString() {
    return "ResolveTask" + getObjectId() + "[" + getValueRequirement() + ", " + getState() + "]";
  }

  @Override
  public int release(final GraphBuildingContext context) {
    int count = super.release(context);
    if (count == 1) {
      // It's possible that only the _requirements collection from the graph builder now holds a reference to us that we care about
      if (!isFinished()) {
        // Only discard unfinished tasks; others might be useful and worth keeping if we can afford the memory
        context.discardTask(this);
      }
    } else if (count == 0) {
      // Nothing holds a reference to us; discard any state remnants
      final State state = getState();
      if (state != null) {
        state.discard(context);
      }
    }
    return count;
  }

}
TOP

Related Classes of com.opengamma.engine.depgraph.ResolveTask$State

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.