ensure right launching sequence for preloaded build processes

This commit is contained in:
Eugene Zhuravlev
2014-10-27 16:41:47 +01:00
parent 29c1bfdc34
commit f78b2033fa
4 changed files with 343 additions and 213 deletions
@@ -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);
}