package jetbrains.mps.ide.make; /*Generated by MPS */ import com.intellij.openapi.progress.Task; import java.util.concurrent.Future; import jetbrains.mps.make.script.IResult; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; import jetbrains.mps.make.service.CoreMakeTask; import org.jetbrains.annotations.Nullable; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; import jetbrains.mps.make.dependencies.MakeSequence; import jetbrains.mps.make.script.IScriptController; import jetbrains.mps.messages.IMessageHandler; import com.intellij.openapi.progress.PerformInBackgroundOption; import com.intellij.openapi.progress.ProgressIndicator; import jetbrains.mps.ide.ThreadUtils; import jetbrains.mps.progress.ProgressMonitorAdapter; import org.jetbrains.mps.openapi.util.ProgressMonitor; import jetbrains.mps.messages.Message; import jetbrains.mps.messages.MessageKind; import java.util.concurrent.ExecutionException; import java.util.concurrent.CancellationException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class MakeTask extends Task.Backgroundable implements Future<IResult> { private final CountDownLatch myLatch = new CountDownLatch(1); private final AtomicReference<MakeTask.TaskState> myState = new AtomicReference<MakeTask.TaskState>(MakeTask.TaskState.NOT_STARTED); private final CoreMakeTask coreTask; private boolean isCanceled = false; public MakeTask(@Nullable Project project, @NotNull String title, MakeSequence makeSeq, IScriptController ctl, IMessageHandler mh, PerformInBackgroundOption bgoption) { super(project, title, true, bgoption); // XXX might be nice to pass CoreMakeTask here, instead of long list of arguments to construct one. // however not it's too much of refactoring for WorkbenchMakeTask coreTask = new MakeTask.WorkbenchMakeTask(title, makeSeq, ctl, mh); } @Override public void run(@NotNull final ProgressIndicator pi) { if (myState.compareAndSet(MakeTask.TaskState.NOT_STARTED, MakeTask.TaskState.RUNNING)) { if (ThreadUtils.isInEDT()) { coreTask.run(new ProgressMonitorAdapter(pi)); } else { this.spawnMakeThreadThenDoRunRelayingLog(new ProgressMonitorAdapter(pi)); } } } private void spawnMakeThreadThenDoRunRelayingLog(final ProgressMonitor monitor) { ThreadGroup tg = new ThreadGroup("MPS Make Thread Group") { @Override public void uncaughtException(Thread thread, Throwable th) { Message msg = new Message(MessageKind.ERROR, String.format("Uncaught %s during make in thread %s", th.getClass().getSimpleName(), thread.getName())); msg.setException(th); coreTask.getMessageHandler().handle(msg); } }; // the flag "daemon" must be set in order for ThreadGroup to be garbage-collected tg.setDaemon(true); Thread makeThread = new Thread(tg, new Runnable() { @Override public void run() { coreTask.run(monitor); } }, "MPS Make Thread"); makeThread.start(); do { try { makeThread.join(); } catch (InterruptedException ie) { } } while (makeThread.isAlive()); } @Override public void onCancel() { isCanceled = true; } @Override public boolean cancel(boolean b) { return false; } @Override public boolean isCancelled() { return myState.get() == MakeTask.TaskState.CANCELLED; } @Override public boolean isDone() { return myState.get() != MakeTask.TaskState.NOT_STARTED && myState.get() != MakeTask.TaskState.RUNNING; } @Override public IResult get() throws InterruptedException, ExecutionException { myLatch.await(); if (myState.get() == MakeTask.TaskState.CANCELLED) { throw new CancellationException(); } return coreTask.getResult(); } @Override public IResult get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { myLatch.await(timeout, unit); if (myState.get() == MakeTask.TaskState.CANCELLED) { throw new CancellationException(); } return coreTask.getResult(); } protected void displayInfo(String info) { } protected void aboutToStart() { } protected void done() { } public class WorkbenchMakeTask extends CoreMakeTask { public WorkbenchMakeTask(@NotNull String title, MakeSequence makeSeq, IScriptController ctl, IMessageHandler mh) { super(title, makeSeq, ctl, mh); } @Override protected void reconcile() { MakeTask.this.myState.set(MakeTask.TaskState.DONE); try { if (isCanceled || coreTask.getResult() == null) { MakeTask.this.myState.set(MakeTask.TaskState.CANCELLED); } super.reconcile(); } finally { myLatch.countDown(); done(); } } @Override protected void doRun(ProgressMonitor monitor) { super.doRun(monitor); MakeTask.this.myState.set(MakeTask.TaskState.INDETERMINATE); } @Override protected void displayInfo(String info) { MakeTask.this.displayInfo(info); } @Override protected void aboutToStart() { MakeTask.this.aboutToStart(); } } private enum TaskState { NOT_STARTED(), RUNNING(), DONE(), CANCELLED(), INDETERMINATE() } }