javac canceling (embedded and external)

checkCanceled convenience method in context
waitFor() API for RequestFuture
This commit is contained in:
Eugene Zhuravlev
2012-02-10 18:05:00 +01:00
parent 4dc22f8c87
commit 2c7fececec
9 changed files with 153 additions and 88 deletions
@@ -284,13 +284,7 @@ public class CompileServerManager implements ApplicationComponent{
}
try {
for (RequestFuture future : futures) {
try {
future.get();
}
catch (InterruptedException ignored) {
}
catch (java.util.concurrent.ExecutionException ignored) {
}
future.waitFor();
}
}
finally {
@@ -447,7 +441,7 @@ public class CompileServerManager implements ApplicationComponent{
connected = client.connect(NetUtils.getLocalHostString(), port);
if (connected) {
final RequestFuture setupFuture = sendSetupRequest(client);
setupFuture.get();
setupFuture.waitFor();
myProcessHandler = processHandler;
myClient = client;
}
@@ -99,7 +99,7 @@ import org.jetbrains.jps.api.RequestFuture;
import java.io.*;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public class CompileDriver {
@@ -591,15 +591,10 @@ public class CompileDriver {
final Set<Artifact> artifacts = ArtifactCompileScope.getArtifactsToBuild(myProject, compileContext.getCompileScope(), true);
final RequestFuture future = compileOnServer(compileContext, modules, artifacts, paths, callback);
if (future != null) {
try {
startCancelWatcher(indicator, future);
future.get();
}
catch (InterruptedException e) {
LOG.error(e); // todo
}
catch (ExecutionException e) {
LOG.error(e); // todo
while (!future.waitFor(200L , TimeUnit.MILLISECONDS)) {
if (indicator.isCanceled()) {
future.cancel(true);
}
}
}
else {
@@ -686,27 +681,6 @@ public class CompileDriver {
});
}
private static void startCancelWatcher(final ProgressIndicator indicator, final RequestFuture future) {
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
public void run() {
while (true) {
try {
Thread.sleep(200L);
if (future.isDone() || future.isCancelled()) {
break;
}
if (indicator.isCanceled()) {
future.cancel(true);
break;
}
}
catch (InterruptedException ignored) {
}
}
}
});
}
private static List<String> fetchFiles(CompileContextImpl context) {
if (context.isRebuild()) {
return Collections.emptyList();
@@ -79,17 +79,34 @@ public class RequestFuture<T> implements Future {
return myDone.get();
}
public Object get() throws InterruptedException, ExecutionException {
while (!isDone()) {
mySemaphore.tryAcquire(100L, TimeUnit.MILLISECONDS);
public void waitFor() {
try {
while (!isDone()) {
mySemaphore.tryAcquire(100L, TimeUnit.MILLISECONDS);
}
}
catch (InterruptedException ignored) {
}
}
public boolean waitFor(long timeout, TimeUnit unit) {
try {
if (!isDone()) {
mySemaphore.tryAcquire(timeout, unit);
}
}
catch (InterruptedException ignored) {
}
return isDone();
}
public Object get() throws InterruptedException, ExecutionException {
waitFor();
return null;
}
public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
if (!isDone()) {
mySemaphore.tryAcquire(timeout, unit);
}
waitFor(timeout, unit);
return null;
}
@@ -23,6 +23,7 @@ import java.util.*;
* Date: 9/17/11
*/
public class CompileContext extends UserDataHolderBase implements MessageHandler{
private static final String CANCELED_MESSAGE = "The build has been canceled";
private final CompileScope myScope;
private final boolean myIsMake;
private final boolean myIsProjectRebuild;
@@ -155,10 +156,20 @@ public class CompileContext extends UserDataHolderBase implements MessageHandler
return myCompilingTests;
}
public CanceledStatus getCancelStatus() {
public final CanceledStatus getCancelStatus() {
return myCancelStatus;
}
public final boolean isCanceled() {
return getCancelStatus().isCanceled();
}
public final void checkCanceled() throws ProjectBuildException {
if (isCanceled()) {
throw new ProjectBuildException(CANCELED_MESSAGE);
}
}
void setCompilingTests(boolean compilingTests) {
myCompilingTests = compilingTests;
}
@@ -24,7 +24,7 @@ import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* @author Eugene Zhuravlev
@@ -34,7 +34,6 @@ public class IncProjectBuilder {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.IncProjectBuilder");
public static final String COMPILE_SERVER_NAME = "COMPILE SERVER";
private static final String CANCELED_MESSAGE = "The build has been canceled";
private final ProjectDescriptor myProjectDescriptor;
private final BuilderRegistry myBuilderRegistry;
@@ -119,11 +118,7 @@ public class IncProjectBuilder {
if (descriptor != null) {
try {
final RequestFuture future = descriptor.client.sendShutdownRequest();
future.get();
}
catch (InterruptedException ignored) {
}
catch (ExecutionException ignored) {
future.waitFor(500L, TimeUnit.MILLISECONDS);
}
finally {
// ensure process is not running
@@ -229,9 +224,7 @@ public class IncProjectBuilder {
// check that output and source roots are not overlapping
final List<File> filesToDelete = new ArrayList<File>();
for (File outputRoot : rootsToDelete) {
if (myCancelStatus.isCanceled()) {
throw new ProjectBuildException(CANCELED_MESSAGE);
}
context.checkCanceled();
boolean okToDelete = true;
if (PathUtil.isUnder(allSourceRoots, outputRoot)) {
okToDelete = false;
@@ -401,9 +394,7 @@ public class IncProjectBuilder {
if (buildResult == ModuleLevelBuilder.ExitCode.ABORT) {
throw new ProjectBuildException("Builder " + builder.getDescription() + " requested build stop");
}
if (myCancelStatus.isCanceled()) {
throw new ProjectBuildException(CANCELED_MESSAGE);
}
context.checkCanceled();
if (buildResult == ModuleLevelBuilder.ExitCode.ADDITIONAL_PASS_REQUIRED) {
if (!nextPassRequired) {
// recalculate basis
@@ -447,9 +438,7 @@ public class IncProjectBuilder {
private void runProjectLevelBuilders(CompileContext context) throws ProjectBuildException {
for (ProjectLevelBuilder builder : myBuilderRegistry.getProjectLevelBuilders()) {
builder.build(context);
if (myCancelStatus.isCanceled()) {
throw new ProjectBuildException(CANCELED_MESSAGE);
}
context.checkCanceled();
}
}
@@ -42,8 +42,8 @@ import java.net.ServerSocket;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @author Eugene Zhuravlev
@@ -263,8 +263,13 @@ public class JavaBuilder extends ModuleLevelBuilder {
final boolean compiledOk = compileJava(chunk, files, classpath, platformCp, sourcePath, outs, context, diagnosticSink, outputSink);
final Map<File, String> chunkSourcePath = ProjectPaths.getSourceRootsWithDependents(chunk, context.isCompilingTests());
context.checkCanceled();
final ClassLoader compiledClassesLoader = createInstrumentationClassLoader(classpath, platformCp, chunkSourcePath, outputSink);
context.checkCanceled();
if (!forms.isEmpty()) {
try {
context.processMessage(new ProgressMessage("Instrumenting forms [" + chunkName + "]"));
@@ -275,6 +280,8 @@ public class JavaBuilder extends ModuleLevelBuilder {
}
}
context.checkCanceled();
if (addNotNullAssertions) {
try {
context.processMessage(new ProgressMessage("Adding NotNull assertions [" + chunkName + "]"));
@@ -285,6 +292,8 @@ public class JavaBuilder extends ModuleLevelBuilder {
}
}
context.checkCanceled();
if (!compiledOk && diagnosticSink.getErrorCount() == 0) {
diagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, "Compilation failed: internal java compiler error"));
}
@@ -356,14 +365,10 @@ public class JavaBuilder extends ModuleLevelBuilder {
final RequestFuture<JavacServerResponseHandler> future = client.sendCompileRequest(
options, files, classpath, platformCp, sourcePath, outs, diagnosticSink, classesConsumer
);
try {
future.get();
}
catch (InterruptedException e) {
e.printStackTrace(System.err);
}
catch (ExecutionException e) {
e.printStackTrace(System.err);
while (!future.waitFor(100L, TimeUnit.MILLISECONDS)) {
if (context.isCanceled()) {
future.cancel(true);
}
}
rc = future.getResponseHandler().isTerminatedSuccessfully();
}
@@ -66,6 +66,18 @@ class JavacFileManager extends ForwardingJavaFileManager<StandardJavaFileManager
return super.isSameFile(a, b);
}
@Override
public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
checkCanceled();
return super.getFileForInput(location, packageName, relativeName);
}
@Override
public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind) throws IOException {
checkCanceled();
return super.getJavaFileForInput(location, className, kind);
}
public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
if (kind != JavaFileObject.Kind.SOURCE && kind != JavaFileObject.Kind.CLASS) {
throw new IllegalArgumentException("Invalid kind " + kind);
@@ -86,6 +98,8 @@ class JavacFileManager extends ForwardingJavaFileManager<StandardJavaFileManager
}
private OutputFileObject getFileForOutput(Location location, JavaFileObject.Kind kind, String fileName, @Nullable String className, FileObject sibling) throws IOException {
checkCanceled();
JavaFileObject src = null;
if (sibling instanceof JavaFileObject) {
final JavaFileObject javaFileObject = (JavaFileObject)sibling;
@@ -190,4 +204,18 @@ class JavacFileManager extends ForwardingJavaFileManager<StandardJavaFileManager
return name.toString().replace('.', File.separatorChar);
}
private int myChecksCounter = 0;
private void checkCanceled() {
final int counter = (myChecksCounter + 1) % 10;
myChecksCounter = counter;
if (counter == 0 && myContext.isCanceled()) {
throw new RuntimeException("Compilation canceled") {
@Override
public Throwable fillInStackTrace() {
return this;
}
};
}
}
}
@@ -2,7 +2,6 @@ package org.jetbrains.jps.javac;
import com.intellij.openapi.util.SystemInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.api.CanceledStatus;
import org.jetbrains.jps.server.ClasspathBootstrap;
@@ -29,13 +28,14 @@ public class JavacMain {
Collection<File> sourcePath,
Map<File, Set<File>> outputDirToRoots,
final DiagnosticOutputConsumer outConsumer,
final OutputFileConsumer outputSink, @Nullable CanceledStatus canceledStatus) {
final OutputFileConsumer outputSink,
CanceledStatus canceledStatus) {
final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
for (File outputDir : outputDirToRoots.keySet()) {
outputDir.mkdirs();
}
final JavacFileManager fileManager = new JavacFileManager(new ContextImpl(compiler, outConsumer, outputSink));
final JavacFileManager fileManager = new JavacFileManager(new ContextImpl(compiler, outConsumer, outputSink, canceledStatus));
fileManager.handleOption("-bootclasspath", Collections.singleton("").iterator()); // this will clear cached stuff
fileManager.handleOption("-extdirs", Collections.singleton("").iterator()); // this will clear cached stuff
@@ -111,10 +111,15 @@ public class JavacMain {
private final StandardJavaFileManager myStdManager;
private final DiagnosticOutputConsumer myOutConsumer;
private final OutputFileConsumer myOutputFileSink;
private final CanceledStatus myCanceledStatus;
public ContextImpl(@NotNull JavaCompiler compiler, @NotNull DiagnosticOutputConsumer outConsumer, @NotNull OutputFileConsumer sink) {
public ContextImpl(@NotNull JavaCompiler compiler,
@NotNull DiagnosticOutputConsumer outConsumer,
@NotNull OutputFileConsumer sink,
CanceledStatus canceledStatus) {
myOutConsumer = outConsumer;
myOutputFileSink = sink;
myCanceledStatus = canceledStatus;
StandardJavaFileManager stdManager = null;
final Class<StandardJavaFileManager> optimizedManagerClass = ClasspathBootstrap.getOptimizedFileManagerClass();
if (optimizedManagerClass != null) {
@@ -136,7 +141,7 @@ public class JavacMain {
}
public boolean isCanceled() {
return false; // todo
return myCanceledStatus.isCanceled();
}
public StandardJavaFileManager getStandardFileManager() {
@@ -11,9 +11,9 @@ import org.jboss.netty.handler.codec.protobuf.ProtobufEncoder;
import org.jboss.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import org.jboss.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.api.CanceledStatus;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import javax.tools.*;
import java.io.File;
import java.net.InetSocketAddress;
import java.util.*;
@@ -32,10 +32,11 @@ public class JavacServer {
private final ChannelGroup myAllOpenChannels = new DefaultChannelGroup("javac-server");
private final ChannelFactory myChannelFactory;
private final ChannelPipelineFactory myPipelineFactory;
private ExecutorService myThreadPool;
public JavacServer() {
final ExecutorService threadPool = Executors.newCachedThreadPool();
myChannelFactory = new NioServerSocketChannelFactory(threadPool, threadPool, 1);
myThreadPool = Executors.newCachedThreadPool();
myChannelFactory = new NioServerSocketChannelFactory(myThreadPool, myThreadPool, 1);
final ChannelRegistrar channelRegistrar = new ChannelRegistrar();
final ChannelHandler compilationRequestsHandler = new CompilationRequestsHandler();
myPipelineFactory = new ChannelPipelineFactory() {
@@ -103,7 +104,15 @@ public class JavacServer {
}
public static JavacRemoteProto.Message compile(final ChannelHandlerContext ctx, final UUID sessionId, List<String> options, Collection<File> files, Collection<File> classpath, Collection<File> platformCp, Collection<File> sourcePath, Map<File, Set<File>> outs) {
public static JavacRemoteProto.Message compile(final ChannelHandlerContext ctx,
final UUID sessionId,
List<String> options,
Collection<File> files,
Collection<File> classpath,
Collection<File> platformCp,
Collection<File> sourcePath,
Map<File, Set<File>> outs,
final CanceledStatus canceledStatus) {
final DiagnosticOutputConsumer diagnostic = new DiagnosticOutputConsumer() {
public void outputLineAvailable(String line) {
Channels.write(ctx.getChannel(), JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createStdOutputResponse(line)));
@@ -122,7 +131,7 @@ public class JavacServer {
};
try {
final boolean rc = JavacMain.compile(options, files, classpath, platformCp, sourcePath, outs, diagnostic, outputSink, null/*todo*/);
final boolean rc = JavacMain.compile(options, files, classpath, platformCp, sourcePath, outs, diagnostic, outputSink, canceledStatus);
return JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createBuildCompletedResponse(rc));
}
catch (Throwable e) {
@@ -131,8 +140,14 @@ public class JavacServer {
}
}
public static void cancelBuild() {
// todo
private final Set<CancelHandler> myCancelHandlers = Collections.synchronizedSet(new HashSet<CancelHandler>());
public void cancelBuilds() {
synchronized (myCancelHandlers) {
for (CancelHandler handler : myCancelHandlers) {
handler.cancel();
}
}
}
private static List<File> toFiles(List<String> paths) {
@@ -145,7 +160,7 @@ public class JavacServer {
private class CompilationRequestsHandler extends SimpleChannelHandler {
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
public void messageReceived(final ChannelHandlerContext ctx, MessageEvent e) throws Exception {
final JavacRemoteProto.Message msg = (JavacRemoteProto.Message)e.getMessage();
final UUID sessionId = JavacProtoUtil.fromProtoUUID(msg.getSessionId());
final JavacRemoteProto.Message.Type messageType = msg.getMessageType();
@@ -172,14 +187,26 @@ public class JavacServer {
outs.put(new File(outputGroup.getOutputRoot()), srcRoots);
}
reply = compile(ctx, sessionId, options, files, cp, platformCp, srcPath, outs);
final CancelHandler cancelHandler = new CancelHandler();
myCancelHandlers.add(cancelHandler);
myThreadPool.submit(new Runnable() {
public void run() {
try {
final JavacRemoteProto.Message exitMsg = compile(ctx, sessionId, options, files, cp, platformCp, srcPath, outs, cancelHandler);
Channels.write(ctx.getChannel(), exitMsg);
}
finally {
myCancelHandlers.remove(cancelHandler);
}
}
});
}
else if (requestType == JavacRemoteProto.Message.Request.Type.CANCEL){
cancelBuild();
cancelBuilds();
reply = JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createRequestAckResponse());
}
else if (requestType == JavacRemoteProto.Message.Request.Type.SHUTDOWN){
cancelBuild();
cancelBuilds();
new Thread("StopThread") {
public void run() {
JavacServer.this.stop();
@@ -213,4 +240,19 @@ public class JavacServer {
super.channelOpen(ctx, e);
}
}
private static class CancelHandler implements CanceledStatus {
private volatile boolean myIsCanceled = false;
private CancelHandler() {
}
public void cancel() {
myIsCanceled = true;
}
public boolean isCanceled() {
return myIsCanceled;
}
}
}