/**
* 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.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.opengamma.engine.function.exclusion.FunctionExclusionGroups;
import com.opengamma.engine.target.digest.TargetDigests;
import com.opengamma.util.MdcAwareThreadPoolExecutor;
/**
* Constructs {@link DependencyGraphBuilder} instances with common parameters. All dependency graph builders created by a single factory will share the same additional thread allowance.
*/
public class DependencyGraphBuilderFactory {
private static final Logger s_logger = LoggerFactory.getLogger(DependencyGraphBuilderFactory.class);
private static final ThreadPoolExecutor s_executor = new MdcAwareThreadPoolExecutor(new ThreadFactory() {
private final AtomicInteger _nextJobThreadId = new AtomicInteger();
@Override
public Thread newThread(final Runnable r) {
final Thread t = new Thread(r) {
@Override
public void run() {
s_logger.info("Starting background thread {}", this);
super.run();
s_logger.info("Finished background thread {}", this);
}
};
t.setDaemon(true);
t.setName(DependencyGraphBuilder.class.getSimpleName() + "-" + _nextJobThreadId.incrementAndGet());
return t;
}
});
private int _maxAdditionalThreadsPerBuilder = DependencyGraphBuilder.getDefaultMaxAdditionalThreads();
private int _maxAdditionalThreads = DependencyGraphBuilder.getDefaultMaxAdditionalThreads();
private boolean _enableFailureReporting = System.getProperty("DependencyGraphBuilderFactory.enableFailureReporting", "FALSE").equalsIgnoreCase("TRUE");
private RunQueueFactory _runQueue = DependencyGraphBuilder.getDefaultRunQueueFactory();
private FunctionExclusionGroups _functionExclusionGroups;
private TargetDigests _targetDigests;
private ComputationTargetCollapser _computationTargetCollapser;
private final Executor _executor = createExecutor();
public DependencyGraphBuilderFactory() {
}
public void setMaxAdditionalThreadsPerBuilder(final int maxAdditionalThreadsPerBuilder) {
_maxAdditionalThreadsPerBuilder = maxAdditionalThreadsPerBuilder;
}
public int getMaxAdditionalThreadsPerBuilder() {
return _maxAdditionalThreadsPerBuilder;
}
public void setMaxAdditionalThreads(final int maxAdditionalThreads) {
_maxAdditionalThreads = maxAdditionalThreads;
}
public int getMaxAdditionalThreads() {
return _maxAdditionalThreads;
}
/**
* Set whether the graph building algorithm should retain information about failed productions and backtracking options to produce more thorough details of why requirements could not be specified.
* Enabling this will increase the memory footprint of the graph building algorithm. The default setting is taken from system property {@code DependencyGraphBuilderFactory.enableFailureReporting} if
* set, otherwise it is off.
*
* @param enableFailureReporting true to enable, false to disable
*/
public void setEnableFailureReporting(final boolean enableFailureReporting) {
_enableFailureReporting = enableFailureReporting;
}
public boolean isEnableFailureReporting() {
return _enableFailureReporting;
}
public void setRunQueueFactory(final RunQueueFactory runQueue) {
_runQueue = runQueue;
}
public RunQueueFactory getRunQueueFactory() {
return _runQueue;
}
public void setFunctionExclusionGroups(final FunctionExclusionGroups functionExclusionGroups) {
_functionExclusionGroups = functionExclusionGroups;
}
public FunctionExclusionGroups getFunctionExclusionGroups() {
return _functionExclusionGroups;
}
public void setTargetDigests(final TargetDigests targetDigests) {
_targetDigests = targetDigests;
}
public TargetDigests getTargetDigests() {
return _targetDigests;
}
public void setComputationTargetCollapser(final ComputationTargetCollapser computationTargetCollapser) {
_computationTargetCollapser = computationTargetCollapser;
}
public ComputationTargetCollapser getComputationTargetCollapser() {
return _computationTargetCollapser;
}
public DependencyGraphBuilder newInstance() {
final DependencyGraphBuilder builder = new DependencyGraphBuilder(getExecutor(), getRunQueueFactory());
configureBuilder(builder);
return builder;
}
protected void configureBuilder(final DependencyGraphBuilder builder) {
builder.setMaxAdditionalThreads(getMaxAdditionalThreadsPerBuilder());
builder.setDisableFailureReporting(!isEnableFailureReporting());
builder.setFunctionExclusionGroups(getFunctionExclusionGroups());
builder.setTargetDigests(getTargetDigests());
builder.setComputationTargetCollapser(getComputationTargetCollapser());
}
protected Executor createExecutor() {
// Wrap calls to the underlying executor so that all threads are pooled but a pool is not created for each factory
return new Executor() {
private final AtomicInteger _threads = new AtomicInteger();
private final Queue<Runnable> _commands = new ConcurrentLinkedQueue<Runnable>();
class WrappedRunnable implements Runnable {
private Runnable _command;
public WrappedRunnable(final Runnable command) {
_command = command;
}
@Override
public void run() {
try {
s_logger.debug("Starting job execution");
_command.run();
} finally {
_command = null;
s_logger.debug("Job execution complete");
threadExit();
s_logger.debug("Thread exit complete");
}
}
}
private Runnable wrap(final Runnable command) {
return new WrappedRunnable(command);
}
private void executeImpl(final Runnable command) {
getDefaultExecutor().execute(wrap(command));
}
private void threadExit() {
int threads = _threads.decrementAndGet();
while (threads < getMaxAdditionalThreads()) {
final Runnable command = _commands.poll();
if (command == null) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("No pending commands to run - {}", threads);
}
return;
}
if (s_logger.isDebugEnabled()) {
s_logger.debug("Thread capacity available - {}", threads);
}
threads = _threads.incrementAndGet();
if (threads <= getMaxAdditionalThreads()) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Thread capacity {} acquired for execution", threads);
}
executeImpl(command);
return;
}
if (s_logger.isDebugEnabled()) {
s_logger.debug("Too many threads {} - requeuing job", threads);
}
_commands.add(command);
threads = _threads.decrementAndGet();
}
}
@Override
public void execute(final Runnable command) {
final int threads = _threads.incrementAndGet();
if (threads <= getMaxAdditionalThreads()) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Direct execution - {} threads", threads);
}
executeImpl(command);
return;
}
// Already started too many jobs
if (s_logger.isDebugEnabled()) {
s_logger.debug("Too many threads {} - queuing job", threads);
}
_commands.add(command);
threadExit();
}
};
}
protected static Executor getDefaultExecutor() {
return s_executor;
}
protected Executor getExecutor() {
return _executor;
}
}