mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
ensure right launching sequence for preloaded build processes
This commit is contained in:
@@ -71,9 +71,9 @@ import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
import org.jetbrains.jps.api.BasicFuture;
|
||||
import org.jetbrains.jps.api.CmdlineProtoUtil;
|
||||
import org.jetbrains.jps.api.CmdlineRemoteProto;
|
||||
import org.jetbrains.jps.api.TaskFuture;
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootType;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -138,7 +138,7 @@ public class CompileDriver {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final BasicFuture future = compileInExternalProcess(compileContext, true);
|
||||
final TaskFuture future = compileInExternalProcess(compileContext, true);
|
||||
if (future != null) {
|
||||
while (!future.waitFor(200L, TimeUnit.MILLISECONDS)) {
|
||||
if (indicator.isCanceled()) {
|
||||
@@ -194,7 +194,7 @@ public class CompileDriver {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BasicFuture compileInExternalProcess(final @NotNull CompileContextImpl compileContext, final boolean onlyCheckUpToDate)
|
||||
private TaskFuture compileInExternalProcess(final @NotNull CompileContextImpl compileContext, final boolean onlyCheckUpToDate)
|
||||
throws Exception {
|
||||
final CompileScope scope = compileContext.getCompileScope();
|
||||
final Collection<String> paths = CompileScopeUtil.fetchFiles(compileContext);
|
||||
@@ -400,7 +400,7 @@ public class CompileDriver {
|
||||
return;
|
||||
}
|
||||
|
||||
final BasicFuture future = compileInExternalProcess(compileContext, false);
|
||||
final TaskFuture future = compileInExternalProcess(compileContext, false);
|
||||
if (future != null) {
|
||||
while (!future.waitFor(200L, TimeUnit.MILLISECONDS)) {
|
||||
if (indicator.isCanceled()) {
|
||||
|
||||
@@ -100,7 +100,8 @@ import org.jetbrains.jps.cmdline.ClasspathBootstrap;
|
||||
import org.jetbrains.jps.incremental.Utils;
|
||||
import org.jetbrains.jps.model.serialization.JpsGlobalLoader;
|
||||
|
||||
import javax.tools.*;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.ToolProvider;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -108,8 +109,7 @@ import java.net.InetSocketAddress;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.jetbrains.jps.api.CmdlineRemoteProto.Message.ControllerMessage.ParametersMessage.TargetTypeBuildScope;
|
||||
@@ -148,10 +148,10 @@ public class BuildManager implements ApplicationComponent{
|
||||
private final File mySystemDirectory;
|
||||
private final ProjectManager myProjectManager;
|
||||
|
||||
private final Map<BasicFuture, Project> myAutomakeFutures = Collections.synchronizedMap(new HashMap<BasicFuture, Project>());
|
||||
private final Map<TaskFuture, Project> myAutomakeFutures = Collections.synchronizedMap(new HashMap<TaskFuture, Project>());
|
||||
private final Map<String, RequestFuture> myBuildsInProgress = Collections.synchronizedMap(new HashMap<String, RequestFuture>());
|
||||
private final Map<String, Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler>> myPreloadedBuilds =
|
||||
Collections.synchronizedMap(new HashMap<String, Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler>>());
|
||||
private final Map<String, Future<Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler>>> myPreloadedBuilds =
|
||||
Collections.synchronizedMap(new HashMap<String, Future<Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler>>>());
|
||||
private final BuildProcessClasspathManager myClasspathManager = new BuildProcessClasspathManager();
|
||||
private final SequentialTaskExecutor myRequestsProcessor = new SequentialTaskExecutor(PooledThreadExecutor.INSTANCE);
|
||||
private final Map<String, ProjectData> myProjectDataMap = Collections.synchronizedMap(new HashMap<String, ProjectData>());
|
||||
@@ -447,7 +447,7 @@ public class BuildManager implements ApplicationComponent{
|
||||
}
|
||||
final List<TargetTypeBuildScope> scopes = CmdlineProtoUtil.createAllModulesScopes(false);
|
||||
final AutoMakeMessageHandler handler = new AutoMakeMessageHandler(project);
|
||||
final BasicFuture future = scheduleBuild(
|
||||
final TaskFuture future = scheduleBuild(
|
||||
project, false, true, false, scopes, Collections.<String>emptyList(), Collections.<String, String>emptyMap(), handler
|
||||
);
|
||||
if (future != null) {
|
||||
@@ -526,12 +526,12 @@ public class BuildManager implements ApplicationComponent{
|
||||
return false;
|
||||
}
|
||||
|
||||
public Collection<BasicFuture> cancelAutoMakeTasks(Project project) {
|
||||
final Collection<BasicFuture> futures = new SmartList<BasicFuture>();
|
||||
public Collection<TaskFuture> cancelAutoMakeTasks(Project project) {
|
||||
final Collection<TaskFuture> futures = new SmartList<TaskFuture>();
|
||||
synchronized (myAutomakeFutures) {
|
||||
for (Map.Entry<BasicFuture, Project> entry : myAutomakeFutures.entrySet()) {
|
||||
for (Map.Entry<TaskFuture, Project> entry : myAutomakeFutures.entrySet()) {
|
||||
if (entry.getValue().equals(project)) {
|
||||
final BasicFuture future = entry.getKey();
|
||||
final TaskFuture future = entry.getKey();
|
||||
future.cancel(false);
|
||||
futures.add(future);
|
||||
}
|
||||
@@ -542,15 +542,15 @@ public class BuildManager implements ApplicationComponent{
|
||||
|
||||
private void cancelPreloadedBuilds(Project project) {
|
||||
final String projectPath = getProjectPath(project);
|
||||
final Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler> pair = myPreloadedBuilds.remove(projectPath);
|
||||
if (pair != null) {
|
||||
final RequestFuture<PreloadedProcessMessageHandler> future = pair.first;
|
||||
myMessageDispatcher.cancelSession(future.getRequestID());
|
||||
runCommand(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
runCommand(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler> pair = takePreloadedProcess(projectPath);
|
||||
if (pair != null) {
|
||||
final RequestFuture<PreloadedProcessMessageHandler> future = pair.first;
|
||||
myMessageDispatcher.cancelSession(future.getRequestID());
|
||||
// waiting for preloaded process from project's task queue guarantees no build is started for this project
|
||||
// until this one gracefully exits and closes all its storages
|
||||
// until this one gracefully exits and closes all its storages
|
||||
getProjectData(projectPath).taskQueue.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -558,207 +558,221 @@ public class BuildManager implements ApplicationComponent{
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BasicFuture scheduleBuild(
|
||||
private Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler> takePreloadedProcess(String projectPath) {
|
||||
Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler> result;
|
||||
final Future<Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler>> preloadProgress = myPreloadedBuilds.remove(projectPath);
|
||||
try {
|
||||
result = preloadProgress != null ? preloadProgress.get() : null;
|
||||
}
|
||||
catch (Throwable e) {
|
||||
LOG.info(e);
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TaskFuture scheduleBuild(
|
||||
final Project project, final boolean isRebuild, final boolean isMake,
|
||||
final boolean onlyCheckUpToDate, final List<TargetTypeBuildScope> scopes,
|
||||
final Collection<String> paths,
|
||||
final Map<String, String> userData, final DefaultMessageHandler messageHandler) {
|
||||
|
||||
final String projectPath = getProjectPath(project);
|
||||
|
||||
final Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler> preloaded = myPreloadedBuilds.remove(projectPath);
|
||||
final RequestFuture<PreloadedProcessMessageHandler> preloadedFuture = preloaded != null? preloaded.first : null;
|
||||
final boolean usingPreloadedProcess = preloadedFuture != null;
|
||||
|
||||
final UUID sessionId;
|
||||
final BuilderMessageHandler handler = new NotifyingMessageHandler(project, messageHandler, messageHandler instanceof AutoMakeMessageHandler);
|
||||
if (usingPreloadedProcess) {
|
||||
LOG.info("Using preloaded build process to compile " + projectPath);
|
||||
sessionId = preloadedFuture.getRequestID();
|
||||
preloadedFuture.getMessageHandler().setDelegateHandler(handler);
|
||||
}
|
||||
else {
|
||||
sessionId = UUID.randomUUID();
|
||||
}
|
||||
|
||||
try {
|
||||
ensureListening();
|
||||
}
|
||||
catch (Exception e) {
|
||||
final UUID sessionId = UUID.randomUUID(); // the actual session did not start, use random UUID
|
||||
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), null));
|
||||
handler.sessionTerminated(sessionId);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
final RequestFuture<? extends BuilderMessageHandler> future = usingPreloadedProcess? preloadedFuture : new RequestFuture<BuilderMessageHandler>(handler, sessionId, new RequestFuture.CancelAction<BuilderMessageHandler>() {
|
||||
@Override
|
||||
public void cancel(RequestFuture<BuilderMessageHandler> future) throws Exception {
|
||||
myMessageDispatcher.cancelSession(future.getRequestID());
|
||||
final DelegateFuture<BuilderMessageHandler> _future = new DelegateFuture<BuilderMessageHandler>();
|
||||
// by using the same queue that processes events we ensure that
|
||||
// the build will be aware of all events that have happened before this request
|
||||
runCommand(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
final Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler> preloaded = takePreloadedProcess(projectPath);
|
||||
final RequestFuture<PreloadedProcessMessageHandler> preloadedFuture = preloaded != null? preloaded.first : null;
|
||||
final boolean usingPreloadedProcess = preloadedFuture != null;
|
||||
|
||||
final UUID sessionId;
|
||||
if (usingPreloadedProcess) {
|
||||
LOG.info("Using preloaded build process to compile " + projectPath);
|
||||
sessionId = preloadedFuture.getRequestID();
|
||||
preloadedFuture.getMessageHandler().setDelegateHandler(handler);
|
||||
}
|
||||
else {
|
||||
sessionId = UUID.randomUUID();
|
||||
}
|
||||
});
|
||||
// by using the same queue that processes events we ensure that
|
||||
// the build will be aware of all events that have happened before this request
|
||||
runCommand(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!usingPreloadedProcess && (future.isCancelled() || project.isDisposed())) {
|
||||
// in case of preloaded process the process was already running, so the handler will be notified upon process termination
|
||||
handler.sessionTerminated(sessionId);
|
||||
future.setDone();
|
||||
return;
|
||||
}
|
||||
|
||||
final CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings globals =
|
||||
CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings.newBuilder().setGlobalOptionsPath(PathManager.getOptionsPath()).build();
|
||||
CmdlineRemoteProto.Message.ControllerMessage.FSEvent currentFSChanges;
|
||||
final SequentialTaskExecutor projectTaskQueue;
|
||||
synchronized (myProjectDataMap) {
|
||||
final ProjectData data = getProjectData(projectPath);
|
||||
if (isRebuild) {
|
||||
data.dropChanges();
|
||||
}
|
||||
if (IS_UNIT_TEST_MODE) {
|
||||
LOG.info("Scheduling build for " +
|
||||
projectPath +
|
||||
"; CHANGED: " +
|
||||
new HashSet<String>(convertToStringPaths(data.myChanged)) +
|
||||
"; DELETED: " +
|
||||
new HashSet<String>(convertToStringPaths(data.myDeleted)));
|
||||
}
|
||||
currentFSChanges = data.getAndResetRescanFlag() ? null : data.createNextEvent();
|
||||
projectTaskQueue = data.taskQueue;
|
||||
final RequestFuture<? extends BuilderMessageHandler> future = usingPreloadedProcess? preloadedFuture : new RequestFuture<BuilderMessageHandler>(handler, sessionId, new RequestFuture.CancelAction<BuilderMessageHandler>() {
|
||||
@Override
|
||||
public void cancel(RequestFuture<BuilderMessageHandler> future) throws Exception {
|
||||
myMessageDispatcher.cancelSession(future.getRequestID());
|
||||
}
|
||||
});
|
||||
_future.setDelegate(future);
|
||||
|
||||
final CmdlineRemoteProto.Message.ControllerMessage params;
|
||||
if (!usingPreloadedProcess && (future.isCancelled() || project.isDisposed())) {
|
||||
// in case of preloaded process the process was already running, so the handler will be notified upon process termination
|
||||
handler.sessionTerminated(sessionId);
|
||||
((BasicFuture)future).setDone();
|
||||
return;
|
||||
}
|
||||
|
||||
final CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings globals =
|
||||
CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings.newBuilder().setGlobalOptionsPath(PathManager.getOptionsPath()).build();
|
||||
CmdlineRemoteProto.Message.ControllerMessage.FSEvent currentFSChanges;
|
||||
final SequentialTaskExecutor projectTaskQueue;
|
||||
synchronized (myProjectDataMap) {
|
||||
final ProjectData data = getProjectData(projectPath);
|
||||
if (isRebuild) {
|
||||
params = CmdlineProtoUtil.createBuildRequest(projectPath, scopes, Collections.<String>emptyList(), userData, globals, null);
|
||||
data.dropChanges();
|
||||
}
|
||||
else if (onlyCheckUpToDate) {
|
||||
params = CmdlineProtoUtil.createUpToDateCheckRequest(projectPath, scopes, paths, userData, globals, currentFSChanges);
|
||||
}
|
||||
else {
|
||||
params = CmdlineProtoUtil.createBuildRequest(projectPath, scopes, isMake ? Collections.<String>emptyList() : paths, userData, globals, currentFSChanges);
|
||||
}
|
||||
if (!usingPreloadedProcess) {
|
||||
myMessageDispatcher.registerBuildMessageHandler(future, params);
|
||||
if (IS_UNIT_TEST_MODE) {
|
||||
LOG.info("Scheduling build for " +
|
||||
projectPath +
|
||||
"; CHANGED: " +
|
||||
new HashSet<String>(convertToStringPaths(data.myChanged)) +
|
||||
"; DELETED: " +
|
||||
new HashSet<String>(convertToStringPaths(data.myDeleted)));
|
||||
}
|
||||
currentFSChanges = data.getAndResetRescanFlag() ? null : data.createNextEvent();
|
||||
projectTaskQueue = data.taskQueue;
|
||||
}
|
||||
|
||||
try {
|
||||
projectTaskQueue.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Throwable execFailure = null;
|
||||
try {
|
||||
if (project.isDisposed()) {
|
||||
if (usingPreloadedProcess) {
|
||||
future.cancel(true);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
myBuildsInProgress.put(projectPath, future);
|
||||
final OSProcessHandler processHandler;
|
||||
final StringBuilder errorsOnLaunch = new StringBuilder();
|
||||
final CmdlineRemoteProto.Message.ControllerMessage params;
|
||||
if (isRebuild) {
|
||||
params = CmdlineProtoUtil.createBuildRequest(projectPath, scopes, Collections.<String>emptyList(), userData, globals, null);
|
||||
}
|
||||
else if (onlyCheckUpToDate) {
|
||||
params = CmdlineProtoUtil.createUpToDateCheckRequest(projectPath, scopes, paths, userData, globals, currentFSChanges);
|
||||
}
|
||||
else {
|
||||
params = CmdlineProtoUtil.createBuildRequest(projectPath, scopes, isMake ? Collections.<String>emptyList() : paths, userData, globals, currentFSChanges);
|
||||
}
|
||||
if (!usingPreloadedProcess) {
|
||||
myMessageDispatcher.registerBuildMessageHandler(future, params);
|
||||
}
|
||||
|
||||
try {
|
||||
projectTaskQueue.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Throwable execFailure = null;
|
||||
try {
|
||||
if (project.isDisposed()) {
|
||||
if (usingPreloadedProcess) {
|
||||
final boolean paramsSent = myMessageDispatcher.sendBuildParameters(future.getRequestID(), params);
|
||||
if (!paramsSent) {
|
||||
myMessageDispatcher.cancelSession(future.getRequestID());
|
||||
}
|
||||
processHandler = preloaded.second;
|
||||
future.cancel(true);
|
||||
}
|
||||
else {
|
||||
processHandler = launchBuildProcess(project, myListenPort, sessionId, false);
|
||||
processHandler.addProcessListener(new ProcessAdapter() {
|
||||
@Override
|
||||
public void onTextAvailable(ProcessEvent event, Key outputType) {
|
||||
if (ProcessOutputTypes.STDERR.equals(outputType)) {
|
||||
if (errorsOnLaunch.length() < 1024) {
|
||||
final String text = event.getText();
|
||||
if (!StringUtil.isEmptyOrSpaces(text)) {
|
||||
errorsOnLaunch.append(text);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
myBuildsInProgress.put(projectPath, future);
|
||||
final OSProcessHandler processHandler;
|
||||
final StringBuilder errorsOnLaunch = new StringBuilder();
|
||||
if (usingPreloadedProcess) {
|
||||
final boolean paramsSent = myMessageDispatcher.sendBuildParameters(future.getRequestID(), params);
|
||||
if (!paramsSent) {
|
||||
myMessageDispatcher.cancelSession(future.getRequestID());
|
||||
}
|
||||
processHandler = preloaded.second;
|
||||
}
|
||||
else {
|
||||
processHandler = launchBuildProcess(project, myListenPort, sessionId, false);
|
||||
processHandler.addProcessListener(new ProcessAdapter() {
|
||||
@Override
|
||||
public void onTextAvailable(ProcessEvent event, Key outputType) {
|
||||
if (ProcessOutputTypes.STDERR.equals(outputType)) {
|
||||
if (errorsOnLaunch.length() < 1024) {
|
||||
final String text = event.getText();
|
||||
if (!StringUtil.isEmptyOrSpaces(text)) {
|
||||
errorsOnLaunch.append(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
processHandler.startNotify();
|
||||
}
|
||||
|
||||
final boolean terminated = processHandler.waitFor();
|
||||
if (terminated) {
|
||||
final int exitValue = processHandler.getProcess().exitValue();
|
||||
if (exitValue != 0) {
|
||||
final StringBuilder msg = new StringBuilder();
|
||||
msg.append("Abnormal build process termination: ");
|
||||
if (errorsOnLaunch.length() > 0) {
|
||||
msg.append("\n").append(errorsOnLaunch);
|
||||
}
|
||||
else {
|
||||
msg.append("unknown error");
|
||||
}
|
||||
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(msg.toString(), null));
|
||||
}
|
||||
}
|
||||
else {
|
||||
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure("Disconnected from build process", null));
|
||||
});
|
||||
processHandler.startNotify();
|
||||
}
|
||||
|
||||
final boolean terminated = processHandler.waitFor();
|
||||
if (terminated) {
|
||||
final int exitValue = processHandler.getProcess().exitValue();
|
||||
if (exitValue != 0) {
|
||||
final StringBuilder msg = new StringBuilder();
|
||||
msg.append("Abnormal build process termination: ");
|
||||
if (errorsOnLaunch.length() > 0) {
|
||||
msg.append("\n").append(errorsOnLaunch);
|
||||
}
|
||||
else {
|
||||
msg.append("unknown error");
|
||||
}
|
||||
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(msg.toString(), null));
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
execFailure = e;
|
||||
}
|
||||
finally {
|
||||
myBuildsInProgress.remove(projectPath);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (Registry.is("compiler.process.preload") && !project.isDisposed()) {
|
||||
try {
|
||||
final Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler> pair = launchPreloadedBuildProcess(project);
|
||||
myPreloadedBuilds.put(projectPath, pair);
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.info("Error pre-loading build process for project " + projectPath, e);
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure("Disconnected from build process", null));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Throwable e) {
|
||||
final BuilderMessageHandler unregistered = myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
|
||||
if (unregistered != null) {
|
||||
unregistered.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), e));
|
||||
unregistered.sessionTerminated(sessionId);
|
||||
catch (Throwable e) {
|
||||
execFailure = e;
|
||||
}
|
||||
finally {
|
||||
myBuildsInProgress.remove(projectPath);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (Registry.is("compiler.process.preload") && !project.isDisposed()) {
|
||||
runCommand(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
final Future<Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler>> preloadResult = launchPreloadedBuildProcess(project, projectTaskQueue);
|
||||
myPreloadedBuilds.put(projectPath, preloadResult);
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.info("Error pre-loading build process for project " + projectPath, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Throwable e) {
|
||||
final BuilderMessageHandler unregistered = myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
|
||||
if (unregistered != null) {
|
||||
unregistered.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), e));
|
||||
unregistered.sessionTerminated(sessionId);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return future;
|
||||
}
|
||||
catch (Throwable e) {
|
||||
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), e));
|
||||
handler.sessionTerminated(sessionId);
|
||||
}
|
||||
|
||||
return null;
|
||||
return _future;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -798,37 +812,42 @@ public class BuildManager implements ApplicationComponent{
|
||||
return "com.intellij.compiler.server.BuildManager";
|
||||
}
|
||||
|
||||
private Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler> launchPreloadedBuildProcess(final Project project) throws Exception {
|
||||
private Future<Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler>> launchPreloadedBuildProcess(final Project project, SequentialTaskExecutor projectTaskQueue) throws Exception {
|
||||
ensureListening();
|
||||
|
||||
final RequestFuture<PreloadedProcessMessageHandler> future = new RequestFuture<PreloadedProcessMessageHandler>(new PreloadedProcessMessageHandler(project), UUID.randomUUID(), new RequestFuture.CancelAction<PreloadedProcessMessageHandler>() {
|
||||
@Override
|
||||
public void cancel(RequestFuture<PreloadedProcessMessageHandler> future) throws Exception {
|
||||
myMessageDispatcher.cancelSession(future.getRequestID());
|
||||
// launching build process from projectTaskQueue ensures that no other build process for this project is currently running
|
||||
return projectTaskQueue.submit(new Callable<Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler>>() {
|
||||
public Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler> call() throws Exception {
|
||||
final RequestFuture<PreloadedProcessMessageHandler> future = new RequestFuture<PreloadedProcessMessageHandler>(new PreloadedProcessMessageHandler(project), UUID.randomUUID(), new RequestFuture.CancelAction<PreloadedProcessMessageHandler>() {
|
||||
@Override
|
||||
public void cancel(RequestFuture<PreloadedProcessMessageHandler> future) throws Exception {
|
||||
myMessageDispatcher.cancelSession(future.getRequestID());
|
||||
}
|
||||
});
|
||||
try {
|
||||
myMessageDispatcher.registerBuildMessageHandler(future, null);
|
||||
final OSProcessHandler processHandler = launchBuildProcess(project, myListenPort, future.getRequestID(), true);
|
||||
processHandler.addProcessListener(new ProcessAdapter() {
|
||||
@Override
|
||||
public void onTextAvailable(ProcessEvent event, Key outputType) {
|
||||
if (ProcessOutputTypes.STDERR.equals(outputType)) {
|
||||
final String text = event.getText();
|
||||
if (!StringUtil.isEmptyOrSpaces(text)) {
|
||||
LOG.info("PRELOADED_BUILD_PROCESS: " + text);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
processHandler.startNotify();
|
||||
return Pair.create(future, processHandler);
|
||||
}
|
||||
catch (ExecutionException e) {
|
||||
myMessageDispatcher.unregisterBuildMessageHandler(future.getRequestID());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
});
|
||||
myMessageDispatcher.registerBuildMessageHandler(future, null);
|
||||
try {
|
||||
final OSProcessHandler processHandler = launchBuildProcess(project, myListenPort, future.getRequestID(), true);
|
||||
processHandler.addProcessListener(new ProcessAdapter() {
|
||||
@Override
|
||||
public void onTextAvailable(ProcessEvent event, Key outputType) {
|
||||
if (ProcessOutputTypes.STDERR.equals(outputType)) {
|
||||
final String text = event.getText();
|
||||
if (!StringUtil.isEmptyOrSpaces(text)) {
|
||||
LOG.info("PRELOADED_BUILD_PROCESS: " + text);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
processHandler.startNotify();
|
||||
return Pair.create(future, processHandler);
|
||||
}
|
||||
catch (ExecutionException e) {
|
||||
myMessageDispatcher.unregisterBuildMessageHandler(future.getRequestID());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private OSProcessHandler launchBuildProcess(Project project, final int port, final UUID sessionId, boolean requestProjectPreload) throws ExecutionException {
|
||||
@@ -1300,7 +1319,7 @@ public class BuildManager implements ApplicationComponent{
|
||||
@Override
|
||||
public void projectClosing(Project project) {
|
||||
cancelPreloadedBuilds(project);
|
||||
for (BasicFuture future : cancelAutoMakeTasks(project)) {
|
||||
for (TaskFuture future : cancelAutoMakeTasks(project)) {
|
||||
future.waitFor(500, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
@@ -1460,5 +1479,88 @@ public class BuildManager implements ApplicationComponent{
|
||||
return "/";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class DelegateFuture<T> implements TaskFuture<T> {
|
||||
@Nullable
|
||||
private TaskFuture<? extends T> myDelegate;
|
||||
private Boolean myRequestedCancelState = null;
|
||||
|
||||
@NotNull
|
||||
public synchronized TaskFuture<? extends T> getDelegate() {
|
||||
TaskFuture<? extends T> delegate = myDelegate;
|
||||
while (delegate == null) {
|
||||
try {
|
||||
wait();
|
||||
}
|
||||
catch (InterruptedException ignored) {
|
||||
}
|
||||
delegate = myDelegate;
|
||||
}
|
||||
return delegate;
|
||||
}
|
||||
|
||||
public synchronized boolean setDelegate(@NotNull TaskFuture<? extends T> delegate) {
|
||||
if (myDelegate == null) {
|
||||
try {
|
||||
myDelegate = delegate;
|
||||
if (myRequestedCancelState != null) {
|
||||
myDelegate.cancel(myRequestedCancelState);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
notifyAll();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public synchronized boolean cancel(boolean mayInterruptIfRunning) {
|
||||
final TaskFuture<? extends T> delegate = myDelegate;
|
||||
if (delegate == null) {
|
||||
myRequestedCancelState = mayInterruptIfRunning;
|
||||
return true;
|
||||
}
|
||||
return delegate.cancel(mayInterruptIfRunning);
|
||||
}
|
||||
|
||||
public void waitFor() {
|
||||
getDelegate().waitFor();
|
||||
}
|
||||
|
||||
public boolean waitFor(long timeout, TimeUnit unit) {
|
||||
return getDelegate().waitFor(timeout, unit);
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
final TaskFuture<? extends T> delegate;
|
||||
synchronized (this) {
|
||||
delegate = myDelegate;
|
||||
if (delegate == null) {
|
||||
return myRequestedCancelState != null;
|
||||
}
|
||||
}
|
||||
return delegate.isCancelled();
|
||||
}
|
||||
|
||||
public boolean isDone() {
|
||||
final TaskFuture<? extends T> delegate;
|
||||
synchronized (this) {
|
||||
delegate = myDelegate;
|
||||
if (delegate == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return delegate.isDone();
|
||||
}
|
||||
|
||||
public T get() throws InterruptedException, java.util.concurrent.ExecutionException {
|
||||
return getDelegate().get();
|
||||
}
|
||||
|
||||
public T get(long timeout, TimeUnit unit) throws InterruptedException, java.util.concurrent.ExecutionException, TimeoutException {
|
||||
return getDelegate().get(timeout, unit);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,14 +15,17 @@
|
||||
*/
|
||||
package org.jetbrains.jps.api;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: 5/3/12
|
||||
*/
|
||||
public class BasicFuture<T> implements Future<T> {
|
||||
public class BasicFuture<T> implements TaskFuture<T> {
|
||||
protected final Semaphore mySemaphore = new Semaphore(1);
|
||||
private final AtomicBoolean myDone = new AtomicBoolean(false);
|
||||
private final AtomicBoolean myCanceledState = new AtomicBoolean(false);
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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 org.jetbrains.jps.api;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public interface TaskFuture<T> extends Future<T> {
|
||||
void waitFor();
|
||||
|
||||
boolean waitFor(long timeout, TimeUnit unit);
|
||||
}
|
||||
Reference in New Issue
Block a user