/*
* Copyright 2003-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.mps.generator.impl;
import jetbrains.mps.generator.GenerationCanceledException;
import jetbrains.mps.generator.GenerationSessionContext;
import jetbrains.mps.generator.impl.IGenerationTaskPool.GenerationTask;
import jetbrains.mps.generator.impl.IGenerationTaskPool.ITaskPoolProvider;
import jetbrains.mps.generator.impl.template.DeltaBuilder;
import jetbrains.mps.generator.runtime.TemplateCreateRootRule;
import jetbrains.mps.generator.runtime.TemplateExecutionEnvironment;
import jetbrains.mps.generator.runtime.TemplateRootMappingRule;
import jetbrains.mps.generator.template.DefaultQueryExecutionContext;
import jetbrains.mps.generator.template.QueryExecutionContext;
import jetbrains.mps.util.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.model.SNodeReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
/**
* Evgeny Gryaznov, Feb 25, 2010
*/
public class ParallelTemplateGenerator extends TemplateGenerator {
private static final boolean ROOT_PER_THREAD = true;
private IGenerationTaskPool myPool;
private List<RootGenerationTask> myTasks;
private Map<Pair<SNode, SNodeReference>, RootGenerationTask> myInputToTask;
private Map<SNode, DefaultQueryExecutionContext> myRootContext;
private final Map<QueryExecutionContext, CompositeGenerationTask> contextToTask = new HashMap<QueryExecutionContext, CompositeGenerationTask>();
public ParallelTemplateGenerator(ITaskPoolProvider taskPoolProvider, GenerationSessionContext operationContext, SModel inputModel, SModel outputModel, StepArguments args) {
super(operationContext, inputModel, outputModel, args);
myTasks = new ArrayList<RootGenerationTask>();
myInputToTask = new ConcurrentHashMap<Pair<SNode, SNodeReference>, RootGenerationTask>();
myPool = taskPoolProvider.getTaskPool();
}
@Override
protected void applyReductions(boolean isPrimary)
throws GenerationCanceledException, GenerationFailureException {
super.applyReductions(isPrimary);
myPool.waitForCompletion();
contextToTask.clear();
myRootContext = null;
for (RootGenerationTask task : myTasks) {
task.registerGeneratedRoot();
}
}
@Override
protected void applyCreateRoot(final TemplateCreateRootRule rule, final TemplateExecutionEnvironment environment) throws GenerationCanceledException, GenerationFailureException {
pushTask(new RootGenerationTask() {
@Override
public void run() throws GenerationCanceledException, GenerationFailureException {
ParallelTemplateGenerator.super.applyCreateRoot(rule, environment);
}
}, new Pair<SNode, SNodeReference>(null, rule.getRuleNode()), environment.getQueryExecutor());
}
@Override
protected void createRootNodeByRule(final TemplateRootMappingRule rule, final SNode inputNode, final TemplateExecutionEnvironmentImpl env, final boolean copyRootOnFailure) throws GenerationCanceledException, GenerationFailureException {
pushTask(new RootGenerationTask() {
@Override
public void run() throws GenerationCanceledException, GenerationFailureException {
ParallelTemplateGenerator.super.createRootNodeByRule(rule, inputNode, env, copyRootOnFailure);
}
}, new Pair<SNode, SNodeReference>(inputNode, rule.getRuleNode()), env.getQueryExecutor());
}
@Override
protected void copyRootInputNode(@NotNull final SNode inputRootNode, @NotNull final TemplateExecutionEnvironmentImpl env) throws GenerationFailureException, GenerationCanceledException {
pushTask(new RootGenerationTask() {
@Override
public void run() throws GenerationCanceledException, GenerationFailureException {
ParallelTemplateGenerator.super.copyRootInputNode(inputRootNode, env);
}
}, new Pair<SNode, SNodeReference>(inputRootNode, null), env.getQueryExecutor());
}
@Override
protected QueryExecutionContext getDefaultExecutionContext(SNode inputNode) {
if (ROOT_PER_THREAD) {
if (inputNode == null || inputNode.getModel() == null) {
return super.getDefaultExecutionContext(null);
}
inputNode = inputNode.getContainingRoot();
if (inputNode.getModel() == getInputModel()) {
DefaultQueryExecutionContext context;
if (myRootContext == null) {
myRootContext = new HashMap<SNode, DefaultQueryExecutionContext>();
context = null;
} else {
context = myRootContext.get(inputNode);
}
if (context == null) {
context = new DefaultQueryExecutionContext(this, false);
myRootContext.put(inputNode, context);
}
return context;
}
}
return super.getDefaultExecutionContext(inputNode);
}
@Override
protected DeltaBuilder createDeltaBuilder() {
return DeltaBuilder.newConcurrentDeltaBuilder();
}
private void pushTask(RootGenerationTask task, Pair<SNode, SNodeReference> pair, QueryExecutionContext executionContext) {
myInputToTask.put(pair, task);
myTasks.add(task);
if (executionContext.isMultithreaded()) {
myPool.addTask(task);
} else {
runTaskWithContext(task, executionContext);
}
}
private void runTaskWithContext(GenerationTask task, QueryExecutionContext executionContext) {
CompositeGenerationTask compositeTask;
synchronized (contextToTask) {
compositeTask = contextToTask.get(executionContext);
if (compositeTask != null && compositeTask.addTask(task)) {
return;
}
compositeTask = new CompositeGenerationTask();
compositeTask.addTask(task);
contextToTask.put(executionContext, compositeTask);
}
myPool.addTask(compositeTask);
}
@Override
protected void registerRoot(GeneratedRootDescriptor rd) {
// FIXME chances are GRD for a root mapping rule doesn't list templateNode/rule, e.g. when rule had failed and we copied root instead
RootGenerationTask task = myInputToTask.get(new Pair<SNode, SNodeReference>(rd.myInputNode, rd.myTemplateNode));
if (task == null) {
getLogger().error(rd.myTemplateNode, "internal: cannot find task for generated root", GeneratorUtil.describe(rd.myInputNode, "input node"));
} else {
task.addGeneratedRoot(rd);
}
}
public abstract class RootGenerationTask implements GenerationTask {
List<GeneratedRootDescriptor> generated = null;
public void addGeneratedRoot(GeneratedRootDescriptor descriptor) {
if (generated == null) {
generated = Collections.singletonList(descriptor);
} else if (generated.size() == 1) {
generated = new ArrayList<GeneratedRootDescriptor>(generated);
generated.add(descriptor);
} else {
generated.add(descriptor);
}
}
public void registerGeneratedRoot() {
if (generated == null) {
return;
}
for (GeneratedRootDescriptor descriptor : generated) {
ParallelTemplateGenerator.super.registerRoot(descriptor);
}
}
}
public static class CompositeGenerationTask implements GenerationTask {
private final Queue<GenerationTask> list = new LinkedList<GenerationTask>();
private boolean isInShutdownMode = false;
public synchronized boolean addTask(GenerationTask task) {
if (isInShutdownMode) {
return false;
}
return list.add(task);
}
private synchronized GenerationTask next() {
if (list.isEmpty()) {
isInShutdownMode = true;
return null;
}
return list.remove();
}
@Override
public void run() throws GenerationCanceledException, GenerationFailureException {
GenerationTask next;
while ((next = next()) != null) {
next.run();
}
}
}
}