fixed ProgressUtils: removed runWithWritePriority since it is inherently racy, remove listener only after Future completion

This commit is contained in:
Alexey Kudravtsev
2014-08-29 13:57:15 +04:00
parent 9247b32062
commit 4a8a160245
3 changed files with 81 additions and 109 deletions
@@ -20,6 +20,7 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.util.ProgressIndicatorBase;
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
import com.intellij.openapi.progress.util.ReadTask;
import com.intellij.openapi.project.DumbService;
@@ -40,7 +41,6 @@ import org.jetbrains.jps.api.CmdlineRemoteProto;
import org.jetbrains.org.objectweb.asm.Opcodes;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author Eugene Zhuravlev
@@ -82,12 +82,7 @@ public abstract class DefaultMessageHandler implements BuilderMessageHandler {
break;
case CONSTANT_SEARCH_TASK:
final CmdlineRemoteProto.Message.BuilderMessage.ConstantSearchTask task = msg.getConstantSearchTask();
myTaskExecutor.submit(new Runnable() {
@Override
public void run() {
handleConstantSearchTask(channel, sessionId, task);
}
});
handleConstantSearchTask(channel, sessionId, task);
break;
}
}
@@ -97,30 +92,29 @@ public abstract class DefaultMessageHandler implements BuilderMessageHandler {
protected abstract void handleBuildEvent(UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.BuildEvent event);
private void handleConstantSearchTask(final Channel channel, final UUID sessionId, final CmdlineRemoteProto.Message.BuilderMessage.ConstantSearchTask task) {
while (true) {
final AtomicBoolean canceled = new AtomicBoolean(false);
DumbService.getInstance(myProject).waitForSmartMode();
ProgressIndicatorUtils.runWithWriteActionPriority(new ReadTask() {
@Override
public void computeInReadAction(@NotNull ProgressIndicator indicator) {
if (DumbService.isDumb(myProject)) {
canceled.set(true);
return;
}
ProgressIndicatorUtils.scheduleWithWriteActionPriority(new ProgressIndicatorBase(), myTaskExecutor, new ReadTask() {
@Override
public void computeInReadAction(@NotNull ProgressIndicator indicator) {
if (DumbService.isDumb(myProject)) {
onCanceled(indicator);
}
else {
doHandleConstantSearchTask(channel, sessionId, task);
}
@Override
public void onCanceled(@NotNull ProgressIndicator indicator) {
canceled.set(true);
}
});
if (!canceled.get()) {
break;
}
}
@Override
public void onCanceled(@NotNull ProgressIndicator indicator) {
DumbService.getInstance(myProject).runWhenSmart(new Runnable() {
@Override
public void run() {
handleConstantSearchTask(channel, sessionId, task);
}
});
}
});
}
private void doHandleConstantSearchTask(Channel channel, UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.ConstantSearchTask task) {
final String ownerClassName = task.getOwnerClassName();
final String fieldName = task.getFieldName();
@@ -22,7 +22,11 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.ui.AppUIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.ide.PooledThreadExecutor;
import java.util.concurrent.Executor;
/**
* @author gregsh
@@ -44,96 +48,76 @@ public class ProgressIndicatorUtils {
return progress;
}
public static void runWithWriteActionPriority(@NotNull final Runnable action) {
runWithWriteActionPriority(new ReadTask() {
@Override
public void computeInReadAction(@NotNull ProgressIndicator indicator) {
action.run();
}
@Override
public void onCanceled(@NotNull ProgressIndicator indicator) {
}
});
public static void scheduleWithWriteActionPriority(@NotNull ReadTask task) {
scheduleWithWriteActionPriority(new ProgressIndicatorBase(), task);
}
public static void runWithWriteActionPriority(@NotNull final ReadTask task) {
runWithWriteActionPriority(new ProgressIndicatorBase(), task);
public static void scheduleWithWriteActionPriority(@NotNull ProgressIndicator progressIndicator, @NotNull ReadTask readTask) {
scheduleWithWriteActionPriority(progressIndicator, PooledThreadExecutor.INSTANCE, readTask);
}
private static void surroundWithListener(@NotNull final ProgressIndicator progressIndicator, @NotNull Runnable runnable) {
final ApplicationAdapter listener = new ApplicationAdapter() {
@Override
public void beforeWriteActionStart(Object action) {
progressIndicator.cancel();
}
};
final Application application = ApplicationManager.getApplication();
application.addApplicationListener(listener);
try {
runnable.run();
}
finally {
application.removeApplicationListener(listener);
}
}
public static void runWithWriteActionPriority(@NotNull final ProgressIndicator progressIndicator, @NotNull final ReadTask task) {
surroundWithListener(progressIndicator, new Runnable() {
public static void scheduleWithWriteActionPriority(@NotNull final ProgressIndicator progressIndicator,
@NotNull final Executor executor,
@NotNull final ReadTask readTask) {
AppUIUtil.invokeOnEdt(new Runnable() {
@Override
public void run() {
runUnderProgress(progressIndicator, task);
final Application application = ApplicationManager.getApplication();
application.assertIsDispatchThread();
final ApplicationAdapter listener = new ApplicationAdapter() {
@Override
public void beforeWriteActionStart(Object action) {
progressIndicator.cancel();
}
};
application.addApplicationListener(listener);
try {
executor.execute(new Runnable() {
@Override
public void run() {
try {
runUnderProgress(progressIndicator, readTask);
}
finally {
application.removeApplicationListener(listener);
}
}
});
}
catch (RuntimeException e) {
application.removeApplicationListener(listener);
throw e;
}
catch (Error e) {
application.removeApplicationListener(listener);
throw e;
}
}
});
}
private static void runUnderProgress(@NotNull final ProgressIndicator progressIndicator, @NotNull final ReadTask task) {
ProgressManager.getInstance().runProcess(new Runnable() {
@Override
public void run() {
// This read action can possible last for a long time, we want it to stop immediately on the first write access.
// For this purpose we launch it under empty progress and invoke progressIndicator#cancel on write access to avoid possible write lock delays.
try {
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
task.computeInReadAction(progressIndicator);
}
});
}
catch (ProcessCanceledException ignore) {
}
finally {
if (progressIndicator.isCanceled()) {
task.onCanceled(progressIndicator);
}
}
}
}, progressIndicator);
}
public static void scheduleWithWriteActionPriority(@NotNull final ReadTask task) {
scheduleWithWriteActionPriority(new ProgressIndicatorBase(), task);
}
public static void scheduleWithWriteActionPriority(@NotNull final ProgressIndicator indicator, @NotNull final ReadTask task) {
// we have to attach listeners in EDT to avoid "fire write action started while attach listeners from another thread" race condition
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
surroundWithListener(indicator, new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
runUnderProgress(indicator, task);
}
});
// This read action can possible last for a long time, we want it to stop immediately on the first write access.
// For this purpose we launch it under empty progress and invoke progressIndicator#cancel on write access to avoid possible write lock delays.
try {
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
task.computeInReadAction(progressIndicator);
}
});
}
catch (ProcessCanceledException ignore) {
}
finally {
if (progressIndicator.isCanceled()) {
task.onCanceled(progressIndicator);
}
});
}
}
});
}, progressIndicator);
}
}
@@ -102,13 +102,7 @@ public class EditorNotificationsImpl extends EditorNotifications {
task.computeInReadAction(indicator);
}
else {
final ProgressIndicator indicator1 = indicator;
myExecutor.execute(new Runnable() {
@Override
public void run() {
ProgressIndicatorUtils.runWithWriteActionPriority(indicator1, task);
}
});
ProgressIndicatorUtils.scheduleWithWriteActionPriority(indicator, myExecutor, task);
}
}
});