Skip to content

Commit 36cf40b

Browse files
committed
Don't update hidden consoles every second
- Added new API to allow console pages knew whether they are visible or not. - Use new IConsole API to schedule console updates only if console page is visible (top level) in the console view. This prevents multiple opened consoles in same console view run update tasks every second, even if no one can see the updated console name. Fixes #2478
1 parent 8c015c9 commit 36cf40b

6 files changed

Lines changed: 205 additions & 27 deletions

File tree

debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ProcessConsole.java

Lines changed: 65 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,8 @@
3737
import java.util.ArrayList;
3838
import java.util.Date;
3939
import java.util.List;
40+
import java.util.concurrent.ExecutorService;
4041
import java.util.concurrent.Executors;
41-
import java.util.concurrent.ScheduledExecutorService;
42-
import java.util.concurrent.ScheduledFuture;
43-
import java.util.concurrent.TimeUnit;
4442
import java.util.regex.Pattern;
4543

4644
import org.eclipse.core.resources.IFile;
@@ -143,9 +141,10 @@ public class ProcessConsole extends IOConsole implements IConsole, IDebugEventSe
143141

144142
private volatile boolean fStreamsClosed;
145143
private volatile boolean disposed;
144+
private volatile boolean isVisible;
146145

147-
private final ScheduledExecutorService consoleNameUpdateExecutor;
148-
private volatile ScheduledFuture<?> pendingNameUpdate;
146+
private final ExecutorService consoleNameUpdateExecutor;
147+
private volatile boolean updateRunning;
149148

150149
/**
151150
* Create process console with default encoding.
@@ -170,7 +169,7 @@ public ProcessConsole(IProcess process, IConsoleColorProvider colorProvider, Str
170169
fAllocateConsole = true;
171170
fProcess = process;
172171
fUserInput = getInputStream();
173-
consoleNameUpdateExecutor = Executors.newSingleThreadScheduledExecutor(r -> {
172+
consoleNameUpdateExecutor = Executors.newSingleThreadExecutor(r -> {
174173
Thread t = new Thread(r, "Console name updater"); //$NON-NLS-1$
175174
t.setDaemon(true);
176175
return t;
@@ -309,9 +308,14 @@ public IPageBookViewPage createPage(IConsoleView view) {
309308
/**
310309
* Computes and returns the current name of this console.
311310
*
311+
* @param triggerAsyncUpdate if <code>true</code> and process is still running,
312+
* triggers asynchronous update of console name every
313+
* second to update elapsed time display in console
314+
* name
315+
*
312316
* @return a name for this console
313317
*/
314-
protected String computeName() {
318+
protected String computeName(boolean triggerAsyncUpdate) {
315319
if (disposed) {
316320
return getName();
317321
}
@@ -337,7 +341,9 @@ protected String computeName() {
337341

338342
// Process is still running, so trigger async update of console name every
339343
// second to keep elapsed time updated.
340-
triggerAsyncConsoleNameUpdate();
344+
if (triggerAsyncUpdate) {
345+
triggerAsyncConsoleNameUpdate(label);
346+
}
341347
return label;
342348
}
343349

@@ -409,24 +415,40 @@ private static String computeProcessLabel(IProcess process, Date launchTime) {
409415
return procLabel;
410416
}
411417

412-
private void triggerAsyncConsoleNameUpdate() {
413-
if (disposed) {
418+
private void triggerAsyncConsoleNameUpdate(String newName) {
419+
if (!canUpdateConsoleName()) {
414420
return;
415421
}
422+
// update console name immediately to show elapsed time right after launch and
423+
// then start async update every second
424+
showName(false, newName);
416425

417-
// refresh every second, but only if not already scheduled:
418-
ScheduledFuture<?> currentPending = pendingNameUpdate;
419-
if (currentPending == null || currentPending.isDone()) {
420-
currentPending = consoleNameUpdateExecutor.schedule(() -> {
421-
pendingNameUpdate = null;
422-
if (disposed) {
423-
return;
426+
synchronized (consoleNameUpdateExecutor) {
427+
if (updateRunning) {
428+
return;
429+
}
430+
updateRunning = true;
431+
consoleNameUpdateExecutor.submit(() -> {
432+
while (canUpdateConsoleName()) {
433+
try {
434+
Thread.sleep(1000);
435+
} catch (InterruptedException e) {
436+
// ignore and continue to update name
437+
}
438+
if (!canUpdateConsoleName()) {
439+
break;
440+
}
441+
showName(false, computeName(false));
424442
}
425-
resetName(false);
426-
}, 1, TimeUnit.SECONDS);
443+
updateRunning = false;
444+
});
427445
}
428446
}
429447

448+
private boolean canUpdateConsoleName() {
449+
return !disposed && isVisible && !fProcess.isTerminated();
450+
}
451+
430452
private static String computeElapsedTimeLabel(Date launchTime, Date terminateTime) {
431453
String elapsedString;
432454
IPreferenceStore store = DebugUIPlugin.getDefault().getPreferenceStore();
@@ -628,7 +650,7 @@ protected void init() {
628650
DebugPlugin.getDefault().addDebugEventListener(this);
629651
// computeName() after addDebugEventListener()
630652
// see https://github.com/eclipse-jdt/eclipse.jdt.debug/issues/390
631-
setName(computeName());
653+
setName(computeName(false));
632654
if (fProcess.isTerminated()) {
633655
closeStreams();
634656
resetName(true);
@@ -681,20 +703,25 @@ public void handleDebugEvents(DebugEvent[] events) {
681703
}
682704

683705
/**
684-
* Compute & update console name, notify listeners if content has changed. Note,
685-
* the {@link #computeName()} method may call this method again asynchronously
686-
* if process is till running to update elapsed time display
706+
* Compute & update console name, notify listeners if content has changed.
707+
*
708+
* @param contentChanged whether the content of console has changed since last
709+
* name update.
687710
*/
688-
private synchronized void resetName(boolean changed) {
689-
final String newName = computeName();
711+
private synchronized void resetName(boolean contentChanged) {
712+
final String newName = computeName(false);
713+
showName(contentChanged, newName);
714+
}
715+
716+
private void showName(boolean contentChanged, final String newName) {
690717
String name = getName();
691718
if (!name.equals(newName)) {
692719
DebugUIPlugin.getStandardDisplay().execute(() -> {
693720
if (disposed) {
694721
return;
695722
}
696723
setName(newName);
697-
if (changed) {
724+
if (contentChanged) {
698725
warnOfContentChange();
699726
}
700727
});
@@ -1215,4 +1242,16 @@ public static String convertElapsedFormat(String humanReadable) {
12151242

12161243
return format.toString();
12171244
}
1245+
1246+
@Override
1247+
public void pageShown() {
1248+
isVisible = true;
1249+
computeName(true);
1250+
}
1251+
1252+
@Override
1253+
public void pageHidden() {
1254+
isVisible = false;
1255+
computeName(false);
1256+
}
12181257
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+
<component id="org.eclipse.ui.console" version="2">
3+
<resource path="src/org/eclipse/ui/console/IConsole.java" type="org.eclipse.ui.console.IConsole">
4+
<filter id="404000815">
5+
<message_arguments>
6+
<message_argument value="org.eclipse.ui.console.IConsole"/>
7+
<message_argument value="pageHidden()"/>
8+
</message_arguments>
9+
</filter>
10+
<filter id="404000815">
11+
<message_arguments>
12+
<message_argument value="org.eclipse.ui.console.IConsole"/>
13+
<message_argument value="pageShown()"/>
14+
</message_arguments>
15+
</filter>
16+
</resource>
17+
</component>

debug/org.eclipse.ui.console/src/org/eclipse/ui/console/IConsole.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,40 @@ public interface IConsole {
9797
*/
9898
String getType();
9999

100+
/**
101+
* Notifies this console that its page has been shown in the UI. This method is
102+
* called when this console page is shown on top of other console pages in at
103+
* least one <b>visible</b> console view.
104+
* <p>
105+
* Default implementation does nothing.
106+
* </p>
107+
* <p>
108+
* Subclasses may override this method to be notified when the console page for
109+
* this console is visible to user.
110+
* </p>
111+
*
112+
* @since 3.16
113+
*/
114+
default void pageShown() {
115+
// Subclasses may override
116+
}
117+
118+
/**
119+
* Notifies this console that its page has been hidden in the UI. This method is
120+
* called when this console page is not shown on top of other console pages in
121+
* any of <b>visible</b> console views.
122+
* <p>
123+
* Default implementation does nothing.
124+
* </p>
125+
* <p>
126+
* Subclasses may override this method to be notified when the console page for
127+
* this console is not more visible to user.
128+
* </p>
129+
*
130+
* @since 3.16
131+
*/
132+
default void pageHidden() {
133+
// Subclasses may override
134+
}
135+
100136
}

debug/org.eclipse.ui.console/src/org/eclipse/ui/console/IConsoleManager.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,37 @@ public interface IConsoleManager {
107107
*/
108108
void refresh(IConsole console);
109109

110+
/**
111+
* Notifies that given console has been shown in the UI (either the view with
112+
* given console on top has been shown or the console has been switched to the
113+
* top console in at least one view).
114+
*
115+
* <p>
116+
* Note, there could be multiple views showing the same console, so the page
117+
* with given console can be still hidden in some views.
118+
* </p>
119+
*
120+
* @param console the top console from view that has been shown
121+
*
122+
* @since 3.16
123+
*/
124+
default void consoleShown(IConsole console) {
125+
}
126+
127+
/**
128+
* Notifies that given console that was on top of at least one view has been
129+
* hidden in the UI (either the console is not the top page in the view or the
130+
* view with given console on top has been hidden).
131+
* <p>
132+
* Note, there could be multiple views showing the same console, so the page
133+
* with given console can be still shown in some views.
134+
* </p>
135+
*
136+
* @param console the console that has been hidden
137+
*
138+
* @since 3.16
139+
*/
140+
default void consoleHidden(IConsole console) {
141+
}
142+
110143
}

debug/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/ConsoleManager.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,4 +479,39 @@ public void refresh(final IConsole console) {
479479
redrawConsoleJob.schedule(50);
480480
}
481481

482+
@Override
483+
public void consoleShown(IConsole console) {
484+
if (isConsoleVisibleSomewhere(console)) {
485+
// if console is already visible in some other view, then there is no need to
486+
// call pageShown() on the console, since user can already see the console
487+
return;
488+
}
489+
console.pageShown();
490+
}
491+
492+
@Override
493+
public void consoleHidden(IConsole console) {
494+
if (isConsoleVisibleSomewhere(console)) {
495+
// if console is still shown in some other view, then there is no need to call
496+
// pageHidden() on the console, since user can still see the console
497+
return;
498+
}
499+
console.pageHidden();
500+
}
501+
502+
private boolean isConsoleVisibleSomewhere(IConsole console) {
503+
synchronized (fConsoleViews) {
504+
for (ConsoleView view : fConsoleViews) {
505+
IWorkbenchPartSite site = view.getSite();
506+
if (site == null) {
507+
continue;
508+
}
509+
boolean viewVisible = site.getPage().isPartVisible(view);
510+
if (viewVisible && view.getConsole() == console) {
511+
return true;
512+
}
513+
}
514+
}
515+
return false;
516+
}
482517
}

debug/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/ConsoleView.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,11 +194,17 @@ protected void showPageRec(PageRec pageRec) {
194194
if (fActive && oldActiveConsole != null) {
195195
deactivateParticipants(oldActiveConsole);
196196
}
197+
198+
setConsole(recConsole);
199+
200+
if (oldActiveConsole != null) {
201+
getConsoleManager().consoleHidden(oldActiveConsole);
202+
}
197203
if (recConsole != null) {
198204
activateParticipants(recConsole);
205+
recConsole.pageShown();
199206
}
200207
}
201-
setConsole(recConsole);
202208
// bring active console on top of stack
203209
if (recConsole != null && !fStack.isEmpty() && !recConsole.equals(fStack.get(0))) {
204210
fStack.remove(recConsole);
@@ -818,10 +824,22 @@ public void partOpened(IWorkbenchPartReference partRef) {
818824

819825
@Override
820826
public void partHidden(IWorkbenchPartReference partRef) {
827+
if (isThisPart(partRef)) {
828+
IConsole console = getConsole();
829+
if (console != null) {
830+
getConsoleManager().consoleHidden(console);
831+
}
832+
}
821833
}
822834

823835
@Override
824836
public void partVisible(IWorkbenchPartReference partRef) {
837+
if (isThisPart(partRef)) {
838+
IConsole console = getConsole();
839+
if (console != null) {
840+
console.pageShown();
841+
}
842+
}
825843
}
826844

827845
@Override

0 commit comments

Comments
 (0)