mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[threading] IJPL-205724: Process events quicker in SuvorovProgress
GitOrigin-RevId: 5a82578094ddbc8029d684a255fcf61c01209b2a
This commit is contained in:
committed by
intellij-monorepo-bot
parent
1d5c3b9e76
commit
143af4abb4
@@ -5,6 +5,7 @@ import com.intellij.ide.IdeEventQueue;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ThreadingSupport;
|
||||
import com.intellij.openapi.application.impl.InternalThreading;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import sun.awt.SunToolkit;
|
||||
@@ -24,25 +25,50 @@ import java.util.function.Consumer;
|
||||
public class EventStealer {
|
||||
private final LinkedBlockingQueue<InputEvent> myInputEvents = new LinkedBlockingQueue<>();
|
||||
private final LinkedBlockingQueue<InvocationEvent> myInvocationEvents = new LinkedBlockingQueue<>();
|
||||
// The ping funcitonality is needed for cases when EDT simply waits for some process that might occasionally send events to EventStealer.
|
||||
// The EDT needs to react to these events ASAP, but not too eagerly, as we'd like to avoid spinning on EDT
|
||||
// So the EDT can simply block on the ping queue with the necessary timeout. It will be woken up as soon as any interesting event appears in the event queue.
|
||||
private final LinkedBlockingQueue<Object> myPingQueue;
|
||||
private final @NotNull Consumer<? super InputEvent> myInputEventDispatcher;
|
||||
|
||||
private static final Object PING = new Object();
|
||||
private static final Logger LOG = Logger.getInstance(EventStealer.class);
|
||||
|
||||
EventStealer(@NotNull Disposable parent, @NotNull Consumer<? super InputEvent> inputConsumer) {
|
||||
this(parent, false, inputConsumer);
|
||||
}
|
||||
|
||||
EventStealer(@NotNull Disposable parent, boolean installPingingQueue, @NotNull Consumer<? super InputEvent> inputConsumer) {
|
||||
myInputEventDispatcher = inputConsumer;
|
||||
IdeEventQueue.getInstance().addPostEventListener(event -> {
|
||||
if (event instanceof MouseEvent me) {
|
||||
myInputEvents.offer(me);
|
||||
ping();
|
||||
return true;
|
||||
}
|
||||
else if (event instanceof KeyEvent ke && event.getID() != KeyEvent.KEY_TYPED) {
|
||||
myInputEvents.offer(ke);
|
||||
ping();
|
||||
return true;
|
||||
}
|
||||
if (event instanceof InvocationEvent ie && isUrgentInvocationEvent(event)) {
|
||||
myInvocationEvents.offer(ie);
|
||||
ping();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}, parent);
|
||||
if (installPingingQueue) {
|
||||
myPingQueue = new LinkedBlockingQueue<>(1);
|
||||
} else {
|
||||
myPingQueue = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void ping() {
|
||||
if (myPingQueue != null) {
|
||||
myPingQueue.offer(PING);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,6 +116,20 @@ public class EventStealer {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
void waitForPing(int timeoutMs) {
|
||||
if (myPingQueue == null) {
|
||||
LOG.error("Ping queue must be installed");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
myPingQueue.poll(timeoutMs, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
// simply resume
|
||||
}
|
||||
}
|
||||
|
||||
void dispatchAllExistingEvents() {
|
||||
while (true) {
|
||||
InvocationEvent event = myInvocationEvents.poll();
|
||||
|
||||
@@ -134,39 +134,38 @@ object SuvorovProgress {
|
||||
val niceOverlay = NiceOverlayUi(rootPane, false)
|
||||
|
||||
val disposable = Disposer.newDisposable()
|
||||
val stealer = PotemkinProgress.startStealingInputEvents(
|
||||
{ event ->
|
||||
var dumpThreads = false
|
||||
if (event is MouseEvent && event.id == MouseEvent.MOUSE_CLICKED) {
|
||||
event.consume()
|
||||
val reaction = niceOverlay.mouseClicked(event.point)
|
||||
when (reaction) {
|
||||
NiceOverlayUi.ClickOutcome.DUMP_THREADS -> dumpThreads = true
|
||||
NiceOverlayUi.ClickOutcome.CLOSED, NiceOverlayUi.ClickOutcome.NOTHING -> Unit
|
||||
}
|
||||
val stealer = EventStealer(disposable, true) { event ->
|
||||
var dumpThreads = false
|
||||
if (event is MouseEvent && event.id == MouseEvent.MOUSE_CLICKED) {
|
||||
event.consume()
|
||||
val reaction = niceOverlay.mouseClicked(event.point)
|
||||
when (reaction) {
|
||||
NiceOverlayUi.ClickOutcome.DUMP_THREADS -> dumpThreads = true
|
||||
NiceOverlayUi.ClickOutcome.CLOSED, NiceOverlayUi.ClickOutcome.NOTHING -> Unit
|
||||
}
|
||||
if (event is MouseEvent && event.id == MouseEvent.MOUSE_MOVED) {
|
||||
event.consume()
|
||||
niceOverlay.mouseMoved(event.point)
|
||||
}
|
||||
if (event is KeyEvent && niceOverlay.dumpThreadsButtonShortcut == KeyStrokeAdapter.getDefaultKeyStroke(event)?.let { KeyboardShortcut(it, null) }) {
|
||||
event.consume()
|
||||
dumpThreads = true
|
||||
}
|
||||
if (dumpThreads) {
|
||||
ApplicationManager.getApplication().executeOnPooledThread(Runnable {
|
||||
val dumpFile = PerformanceWatcher.getInstance().dumpThreads("freeze-popup", true, false)
|
||||
if (dumpFile != null) {
|
||||
if (Files.exists(dumpFile)) {
|
||||
RevealFileAction.openFile(dumpFile)
|
||||
}
|
||||
else {
|
||||
getLogger<SuvorovProgress>().error { "Failed to dump threads to $dumpFile" }
|
||||
}
|
||||
}
|
||||
if (event is MouseEvent && event.id == MouseEvent.MOUSE_MOVED) {
|
||||
event.consume()
|
||||
niceOverlay.mouseMoved(event.point)
|
||||
}
|
||||
if (event is KeyEvent && niceOverlay.dumpThreadsButtonShortcut == KeyStrokeAdapter.getDefaultKeyStroke(event)?.let { KeyboardShortcut(it, null) }) {
|
||||
event.consume()
|
||||
dumpThreads = true
|
||||
}
|
||||
if (dumpThreads) {
|
||||
ApplicationManager.getApplication().executeOnPooledThread(Runnable {
|
||||
val dumpFile = PerformanceWatcher.getInstance().dumpThreads("freeze-popup", true, false)
|
||||
if (dumpFile != null) {
|
||||
if (Files.exists(dumpFile)) {
|
||||
RevealFileAction.openFile(dumpFile)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, disposable)
|
||||
else {
|
||||
getLogger<SuvorovProgress>().error { "Failed to dump threads to $dumpFile" }
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
repostAllEvents()
|
||||
var oldTimestamp = System.currentTimeMillis()
|
||||
@@ -179,7 +178,8 @@ object SuvorovProgress {
|
||||
oldTimestamp = newTimestamp
|
||||
niceOverlay.redrawMainComponent()
|
||||
}
|
||||
stealer.dispatchEvents(10)
|
||||
stealer.dispatchEvents(0)
|
||||
stealer.waitForPing(10)
|
||||
}
|
||||
else {
|
||||
niceOverlay.redrawMainComponent()
|
||||
|
||||
Reference in New Issue
Block a user