diff --git a/java/compiler/impl/src/com/intellij/compiler/CompileServerManager.java b/java/compiler/impl/src/com/intellij/compiler/CompileServerManager.java index ffbcdd9586d9..649db10233dd 100644 --- a/java/compiler/impl/src/com/intellij/compiler/CompileServerManager.java +++ b/java/compiler/impl/src/com/intellij/compiler/CompileServerManager.java @@ -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; } diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java index 857d42b0632b..0b1bf1f4605c 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java @@ -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 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 fetchFiles(CompileContextImpl context) { if (context.isRebuild()) { return Collections.emptyList(); diff --git a/jps/jps-builders/src/org/jetbrains/jps/api/RequestFuture.java b/jps/jps-builders/src/org/jetbrains/jps/api/RequestFuture.java index 7ff90b9cdc7a..7366541b20cd 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/api/RequestFuture.java +++ b/jps/jps-builders/src/org/jetbrains/jps/api/RequestFuture.java @@ -79,17 +79,34 @@ public class RequestFuture 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; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileContext.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileContext.java index f4d3ba5dcce7..90bd4e549c4e 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileContext.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileContext.java @@ -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; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java index 7d65af0c3949..5270a2c7c27a 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java @@ -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 filesToDelete = new ArrayList(); 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(); } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java index acbd1140f795..bfde575cf4d9 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java @@ -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 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 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(); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacFileManager.java b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacFileManager.java index a7aac0fce6dc..73e238f9bdd7 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacFileManager.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacFileManager.java @@ -66,6 +66,18 @@ class JavacFileManager extends ForwardingJavaFileManager sourcePath, Map> 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 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() { diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServer.java b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServer.java index 2b75d6dc7673..537cdf6a64e8 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServer.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServer.java @@ -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 options, Collection files, Collection classpath, Collection platformCp, Collection sourcePath, Map> outs) { + public static JavacRemoteProto.Message compile(final ChannelHandlerContext ctx, + final UUID sessionId, + List options, + Collection files, + Collection classpath, + Collection platformCp, + Collection sourcePath, + Map> 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 myCancelHandlers = Collections.synchronizedSet(new HashSet()); + + public void cancelBuilds() { + synchronized (myCancelHandlers) { + for (CancelHandler handler : myCancelHandlers) { + handler.cancel(); + } + } } private static List toFiles(List 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; + } + } } \ No newline at end of file