mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
runWriteActionWithProgressInBackgroundThread
This commit is contained in:
@@ -30,16 +30,22 @@ public class TestWriteActionUnderProgress extends DumbAwareAction {
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
ApplicationImpl app = (ApplicationImpl)ApplicationManager.getApplication();
|
||||
app.runWriteActionWithProgress("Progress", null, null, null, TestWriteActionUnderProgress::runIndeterminateProgress);
|
||||
app.runWriteActionWithProgress("Cancellable Progress", null, null, "Stop", TestWriteActionUnderProgress::runDeterminateProgress);
|
||||
|
||||
boolean success = app.runWriteActionWithProgressInDispatchThread(
|
||||
"Progress", null, null, null,
|
||||
TestWriteActionUnderProgress::runIndeterminateProgress);
|
||||
assert success;
|
||||
|
||||
app.runWriteActionWithProgressInBackgroundThread("Cancellable Progress", null, null, "Stop", TestWriteActionUnderProgress::runDeterminateProgress);
|
||||
}
|
||||
|
||||
private static void runDeterminateProgress(ProgressIndicator indicator) {
|
||||
indicator.setIndeterminate(false);
|
||||
int iterations = 3000;
|
||||
indicator.setText("");
|
||||
indicator.setText("In background thread");
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
TimeoutUtil.sleep(1);
|
||||
ApplicationManager.getApplication().assertWriteAccessAllowed();
|
||||
indicator.setFraction(((double)i + 1) / ((double)iterations));
|
||||
indicator.setText2(String.valueOf(i));
|
||||
ProgressManager.checkCanceled();
|
||||
@@ -48,9 +54,10 @@ public class TestWriteActionUnderProgress extends DumbAwareAction {
|
||||
|
||||
private static void runIndeterminateProgress(ProgressIndicator indicator) {
|
||||
indicator.setIndeterminate(true);
|
||||
indicator.setText("Indeterminate");
|
||||
indicator.setText("In Event Dispatch thread");
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
TimeoutUtil.sleep(5);
|
||||
ApplicationManager.getApplication().assertWriteAccessAllowed();
|
||||
indicator.checkCanceled();
|
||||
}
|
||||
}
|
||||
|
||||
+45
-15
@@ -72,6 +72,7 @@ import com.intellij.util.concurrency.Semaphore;
|
||||
import com.intellij.util.containers.Stack;
|
||||
import com.intellij.util.io.storage.HeavyProcessLatch;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
@@ -109,6 +110,7 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
|
||||
|
||||
private final Stack<Class> myWriteActionsStack = new Stack<>(); // accessed from EDT only, no need to sync
|
||||
private int myWriteStackBase = 0;
|
||||
private volatile Thread myWriteActionThread;
|
||||
|
||||
private int myInEditorPaintCounter; // EDT only
|
||||
private final long myStartTime;
|
||||
@@ -690,6 +692,10 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
|
||||
@Override
|
||||
@NotNull
|
||||
public ModalityState getCurrentModalityState() {
|
||||
if (Thread.currentThread() == myWriteActionThread) {
|
||||
return getDefaultModalityState();
|
||||
}
|
||||
|
||||
return LaterInvocator.getCurrentModalityState();
|
||||
}
|
||||
|
||||
@@ -942,22 +948,41 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
|
||||
myLock.readUnlock();
|
||||
}
|
||||
|
||||
public boolean runWriteActionWithProgress(@NotNull String title,
|
||||
@Nullable Project project,
|
||||
@Nullable JComponent parentComponent,
|
||||
@Nullable String cancelText,
|
||||
@NotNull Consumer<ProgressIndicator> action) {
|
||||
@ApiStatus.Experimental
|
||||
public boolean runWriteActionWithProgressInDispatchThread(
|
||||
@NotNull String title, @Nullable Project project, @Nullable JComponent parentComponent, @Nullable String cancelText,
|
||||
@NotNull Consumer<ProgressIndicator> action
|
||||
) {
|
||||
Class<?> clazz = action.getClass();
|
||||
startWrite(clazz);
|
||||
try {
|
||||
PotemkinProgress indicator = new PotemkinProgress(title, project, parentComponent, cancelText);
|
||||
try {
|
||||
ProgressManager.getInstance().runProcess(() -> action.consume(indicator), indicator);
|
||||
}
|
||||
catch (ProcessCanceledException ignore) { }
|
||||
finally {
|
||||
indicator.progressFinished();
|
||||
}
|
||||
indicator.runInSwingThread(() -> action.consume(indicator));
|
||||
return !indicator.isCanceled();
|
||||
}
|
||||
finally {
|
||||
endWrite(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
public boolean runWriteActionWithProgressInBackgroundThread(
|
||||
@NotNull String title, @Nullable Project project, @Nullable JComponent parentComponent, @Nullable String cancelText,
|
||||
@NotNull Consumer<ProgressIndicator> action
|
||||
) {
|
||||
Class<?> clazz = action.getClass();
|
||||
startWrite(clazz);
|
||||
try {
|
||||
PotemkinProgress indicator = new PotemkinProgress(title, project, parentComponent, cancelText);
|
||||
indicator.runInBackground(() -> {
|
||||
assert myWriteActionThread == null;
|
||||
myWriteActionThread = Thread.currentThread();
|
||||
try {
|
||||
action.consume(indicator);
|
||||
} finally {
|
||||
myWriteActionThread = null;
|
||||
}
|
||||
});
|
||||
return !indicator.isCanceled();
|
||||
}
|
||||
finally {
|
||||
@@ -1035,7 +1060,10 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
|
||||
|
||||
@Override
|
||||
public boolean isReadAccessAllowed() {
|
||||
return isDispatchThread() || myLock.isReadLockedByThisThread();
|
||||
if (isDispatchThread()) {
|
||||
return myWriteActionThread == null; // no reading from EDT during background write action
|
||||
}
|
||||
return myLock.isReadLockedByThisThread() || myWriteActionThread == Thread.currentThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1140,7 +1168,9 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
|
||||
}
|
||||
|
||||
private void startWrite(@NotNull Class clazz) {
|
||||
assertIsDispatchThread("Write access is allowed from event dispatch thread only");
|
||||
if (!isWriteAccessAllowed()) {
|
||||
assertIsDispatchThread("Write access is allowed from event dispatch thread only");
|
||||
}
|
||||
HeavyProcessLatch.INSTANCE.stopThreadPrioritizing(); // let non-cancellable read actions complete faster, if present
|
||||
boolean writeActionPending = myWriteActionPending;
|
||||
if (gatherStatistics && myWriteActionsStack.isEmpty() && !writeActionPending) {
|
||||
@@ -1283,7 +1313,7 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
|
||||
|
||||
@Override
|
||||
public boolean isWriteAccessAllowed() {
|
||||
return isDispatchThread() && myLock.isWriteLocked();
|
||||
return isDispatchThread() && myLock.isWriteLocked() || myWriteActionThread == Thread.currentThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+63
-23
@@ -15,11 +15,12 @@
|
||||
*/
|
||||
package com.intellij.openapi.progress.util;
|
||||
|
||||
import com.intellij.concurrency.JobScheduler;
|
||||
import com.intellij.ide.IdeEventQueue;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.util.concurrency.Semaphore;
|
||||
import com.intellij.util.io.storage.HeavyProcessLatch;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -29,30 +30,27 @@ import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* A progress indicator for processes running in EDT. Paints itself in checkCanceled calls.
|
||||
* A progress indicator for write actions. Paints itself explicitly, without resorting to normal Swing's delayed repaint API.
|
||||
* Doesn't dispatch Swing events, except for handling manually those that can cancel it or affect the visual presentation.
|
||||
*
|
||||
* @author peter
|
||||
*/
|
||||
public class PotemkinProgress extends ProgressWindow {
|
||||
private long myLastUiUpdate = System.currentTimeMillis();
|
||||
private final Queue<InputEvent> myEventQueue = new ConcurrentLinkedQueue<>();
|
||||
private final LinkedBlockingQueue<InputEvent> myEventQueue = new LinkedBlockingQueue<>();
|
||||
|
||||
public PotemkinProgress(@NotNull String title, @Nullable Project project, @Nullable JComponent parentComponent, @Nullable String cancelText) {
|
||||
super(cancelText != null,false, project, parentComponent, cancelText);
|
||||
setTitle(title);
|
||||
installCheckCanceledPaintingHook();
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
startStealingInputEvents();
|
||||
}
|
||||
|
||||
private void startStealingInputEvents() {
|
||||
checkNativeEventsRegularly();
|
||||
|
||||
IdeEventQueue.getInstance().addPostEventListener(event -> {
|
||||
if (event instanceof InputEvent) {
|
||||
myEventQueue.offer((InputEvent)event);
|
||||
@@ -62,12 +60,6 @@ public class PotemkinProgress extends ProgressWindow {
|
||||
}, this);
|
||||
}
|
||||
|
||||
private void checkNativeEventsRegularly() {
|
||||
ScheduledFuture<?> future = JobScheduler.getScheduler().scheduleWithFixedDelay(
|
||||
() -> SunToolkit.flushPendingEvents(), 3, 3, TimeUnit.MILLISECONDS);
|
||||
Disposer.register(this, () -> future.cancel(false));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected ProgressDialog getDialog() {
|
||||
@@ -83,7 +75,7 @@ public class PotemkinProgress extends ProgressWindow {
|
||||
@Override
|
||||
public boolean isCanceled() {
|
||||
if (ApplicationManager.getApplication().isDispatchThread()) {
|
||||
dispatchAwtEventsWithoutModelAccess();
|
||||
dispatchAwtEventsWithoutModelAccess(0);
|
||||
updateUI();
|
||||
}
|
||||
return super.isCanceled();
|
||||
@@ -91,12 +83,18 @@ public class PotemkinProgress extends ProgressWindow {
|
||||
});
|
||||
}
|
||||
|
||||
private void dispatchAwtEventsWithoutModelAccess() {
|
||||
while (true) {
|
||||
InputEvent event = myEventQueue.poll();
|
||||
if (event == null) return;
|
||||
private void dispatchAwtEventsWithoutModelAccess(int timeoutMs) {
|
||||
SunToolkit.flushPendingEvents();
|
||||
try {
|
||||
while (true) {
|
||||
InputEvent event = myEventQueue.poll(timeoutMs, TimeUnit.MILLISECONDS);
|
||||
if (event == null) return;
|
||||
|
||||
dispatchInputEvent(event);
|
||||
dispatchInputEvent(event);
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +145,7 @@ public class PotemkinProgress extends ProgressWindow {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void progressFinished() {
|
||||
private void progressFinished() {
|
||||
getDialog().hideImmediately();
|
||||
}
|
||||
|
||||
@@ -163,4 +161,46 @@ public class PotemkinProgress extends ProgressWindow {
|
||||
dialogPanel.paintImmediately(dialogPanel.getBounds());
|
||||
}
|
||||
|
||||
/** Executes the action in EDT, paints itself inside checkCanceled calls. */
|
||||
public void runInSwingThread(@NotNull Runnable action) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
installCheckCanceledPaintingHook();
|
||||
try {
|
||||
ProgressManager.getInstance().runProcess(action, this);
|
||||
}
|
||||
catch (ProcessCanceledException ignore) { }
|
||||
finally {
|
||||
progressFinished();
|
||||
}
|
||||
}
|
||||
|
||||
/** Executes the action in a background thread, block Swing thread, handles selected input events and paints itself periodically. */
|
||||
public void runInBackground(@NotNull Runnable action) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
enterModality();
|
||||
|
||||
try {
|
||||
ensureBackgroundThreadStarted(action);
|
||||
|
||||
while (isRunning()) {
|
||||
dispatchAwtEventsWithoutModelAccess(10);
|
||||
updateUI();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
exitModality();
|
||||
progressFinished();
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureBackgroundThreadStarted(@NotNull Runnable action) {
|
||||
Semaphore started = new Semaphore();
|
||||
started.down();
|
||||
ApplicationManager.getApplication().executeOnPooledThread(() -> ProgressManager.getInstance().runProcess(() -> {
|
||||
started.up();
|
||||
action.run();
|
||||
}, this));
|
||||
|
||||
started.waitFor();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user