com.opengamma.engine.depgraph.ResolveTask.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.engine.depgraph.ResolveTask.java

Source

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

}