1. ensure sessionTerminated is called _after_ all build messages are processed

2. in order not to depend on message processing speed in IDEA, when processing compiler messages, submit them to CompileContext from a separate thread maintaining message order
This commit is contained in:
Eugene Zhuravlev
2012-10-14 20:46:41 +02:00
parent bc4330aac5
commit 162d6a6606
3 changed files with 99 additions and 37 deletions
@@ -86,6 +86,7 @@ import com.intellij.util.Function;
import com.intellij.util.StringBuilderSpinAllocator;
import com.intellij.util.ThrowableRunnable;
import com.intellij.util.concurrency.Semaphore;
import com.intellij.util.concurrency.SequentialTaskExecutor;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.MultiMap;
@@ -104,6 +105,7 @@ import org.jetbrains.jps.incremental.Utils;
import javax.swing.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import static org.jetbrains.jps.api.CmdlineRemoteProto.Message.ControllerMessage.ParametersMessage.TargetTypeBuildScope;
@@ -443,22 +445,36 @@ public class CompileDriver {
final BuildManager buildManager = BuildManager.getInstance();
buildManager.cancelAutoMakeTasks(myProject);
return buildManager.scheduleBuild(myProject, compileContext.isRebuild(), compileContext.isMake(), scopes, paths, builderParams, new DefaultMessageHandler(myProject) {
private final SequentialTaskExecutor myContextUpdater = new SequentialTaskExecutor(new Executor() {
@Override
public void execute(Runnable command) {
ApplicationManager.getApplication().executeOnPooledThread(command);
}
});
@Override
public void buildStarted(UUID sessionId) {
}
@Override
public void sessionTerminated(UUID sessionId) {
public void sessionTerminated(final UUID sessionId) {
if (compileContext.shouldUpdateProblemsView()) {
final ProblemsView view = ProblemsViewImpl.SERVICE.getInstance(myProject);
view.clearProgress();
view.clearOldMessages(compileContext.getCompileScope(), sessionId);
myContextUpdater.execute(new Runnable() {
@Override
public void run() {
if (!myProject.isDisposed()) {
final ProblemsView view = ProblemsViewImpl.SERVICE.getInstance(myProject);
view.clearProgress();
view.clearOldMessages(compileContext.getCompileScope(), sessionId);
}
}
});
}
}
@Override
public void handleFailure(UUID sessionId, CmdlineRemoteProto.Message.Failure failure) {
compileContext.addMessage(CompilerMessageCategory.ERROR, failure.getDescription(), null, -1, -1);
submitMessage(CompilerMessageCategory.ERROR, failure.getDescription(), null, -1, -1);
final String trace = failure.getStacktrace();
if (trace != null) {
LOG.info(trace);
@@ -471,9 +487,10 @@ public class CompileDriver {
protected void handleCompileMessage(UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.CompileMessage message) {
final CmdlineRemoteProto.Message.BuilderMessage.CompileMessage.Kind kind = message.getKind();
//System.out.println(compilerMessage.getText());
final String messageText = message.getText();
if (kind == CmdlineRemoteProto.Message.BuilderMessage.CompileMessage.Kind.PROGRESS) {
final ProgressIndicator indicator = compileContext.getProgressIndicator();
indicator.setText(message.getText());
indicator.setText(messageText);
if (message.hasDone()) {
indicator.setFraction(message.getDone());
}
@@ -489,12 +506,19 @@ public class CompileDriver {
final long line = message.hasLine() ? message.getLine() : -1;
final long column = message.hasColumn() ? message.getColumn() : -1;
final String srcUrl = sourceFilePath != null ? VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, sourceFilePath) : null;
compileContext.addMessage(
category, message.getText(), srcUrl, (int)line, (int)column
);
submitMessage(category, messageText, srcUrl, (int)line, (int)column);
}
}
private void submitMessage(final CompilerMessageCategory category, final String messageText, final String srcUrl, final int line, final int column) {
myContextUpdater.execute(new Runnable() {
@Override
public void run() {
compileContext.addMessage(category, messageText, srcUrl, line, column);
}
});
}
@Override
protected void handleBuildEvent(UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.BuildEvent event) {
final CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.Type eventType = event.getEventType();
@@ -506,16 +506,25 @@ public class BuildManager implements ApplicationComponent{
CmdlineProtoUtil.createForceCompileRequest(projectPath, scopes, paths, userData, globals, currentFSChanges);
}
myMessageDispatcher.registerBuildMessageHandler(sessionId, handler, params);
myMessageDispatcher.registerBuildMessageHandler(sessionId, new BuilderMessageHandlerWrapper(handler) {
@Override
public void sessionTerminated(UUID sessionId) {
try {
super.sessionTerminated(sessionId);
}
finally {
future.setDone();
}
}
}, params);
try {
projectTaskQueue.submit(new Runnable() {
@Override
public void run() {
ExecutionException execFailure = null;
try {
if (project.isDisposed()) {
myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
handler.sessionTerminated(sessionId);
return;
}
myBuildsInProgress.put(projectPath, future);
@@ -528,14 +537,6 @@ public class BuildManager implements ApplicationComponent{
};
final StringBuilder stdErrOutput = new StringBuilder();
processHandler.addProcessListener(new ProcessAdapter() {
@Override
public void processTerminated(ProcessEvent event) {
final BuilderMessageHandler handler = myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
if (handler != null) {
handler.sessionTerminated(sessionId);
}
}
@Override
public void onTextAvailable(ProcessEvent event, Key outputType) {
// re-translate builder's output to idea.log
@@ -561,30 +562,39 @@ public class BuildManager implements ApplicationComponent{
else {
msg.append("unknown error");
}
future.getMessageHandler().handleFailure(sessionId, CmdlineProtoUtil.createFailure(msg.toString(), null));
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(msg.toString(), null));
}
}
else {
future.getMessageHandler().handleFailure(sessionId, CmdlineProtoUtil.createFailure("Disconnected from build process", null));
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure("Disconnected from build process", null));
}
}
catch (ExecutionException e) {
myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), e));
handler.sessionTerminated(sessionId);
execFailure = e;
}
finally {
myBuildsInProgress.remove(projectPath);
future.setDone();
if (myMessageDispatcher.getAssociatedChannel(sessionId) == null) {
// either the connection has never been established (process not started or execution failed), or no messages were sent from the launched process.
// in this case the session cannot be unregistered by the message dispatcher
final BuilderMessageHandler unregistered = myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
if (unregistered != null) {
if (execFailure != null) {
unregistered.handleFailure(sessionId, CmdlineProtoUtil.createFailure(execFailure.getMessage(), execFailure));
}
unregistered.sessionTerminated(sessionId);
}
}
}
}
});
}
catch (Throwable e) {
myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), e));
handler.sessionTerminated(sessionId);
future.setDone();
final BuilderMessageHandler unregistered = myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
if (unregistered != null) {
unregistered.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), e));
unregistered.sessionTerminated(sessionId);
}
}
}
});
@@ -905,6 +915,34 @@ public class BuildManager implements ApplicationComponent{
return builder.toString();
}
private static class BuilderMessageHandlerWrapper implements BuilderMessageHandler {
private final DefaultMessageHandler myHandler;
public BuilderMessageHandlerWrapper(DefaultMessageHandler handler) {
myHandler = handler;
}
@Override
public void buildStarted(UUID sessionId) {
myHandler.buildStarted(sessionId);
}
@Override
public void handleBuildMessage(Channel channel, UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage msg) {
myHandler.handleBuildMessage(channel, sessionId, msg);
}
@Override
public void handleFailure(UUID sessionId, CmdlineRemoteProto.Message.Failure failure) {
myHandler.handleFailure(sessionId, failure);
}
@Override
public void sessionTerminated(UUID sessionId) {
myHandler.sessionTerminated(sessionId);
}
}
private class ProjectWatcher extends ProjectManagerAdapter {
private final Map<Project, MessageBusConnection> myConnections = new HashMap<Project, MessageBusConnection>();
@@ -61,14 +61,14 @@ class BuildMessageDispatcher extends SimpleChannelHandler {
@Nullable
public Channel getConnectedChannel(final UUID sessionId) {
final Channel channel = getAssociatedChannel(sessionId);
return channel != null && channel.isConnected()? channel : null;
}
@Nullable
public Channel getAssociatedChannel(final UUID sessionId) {
final SessionData data = myMessageHandlers.get(sessionId);
if (data != null) {
final Channel channel = data.channel;
if (channel != null && channel.isConnected()) {
return channel;
}
}
return null;
return data != null? data.channel : null;
}