Skip to content

Commit 3d746e1

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 2b30063 commit 3d746e1

6 files changed

Lines changed: 205 additions & 30 deletions

File tree

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

Lines changed: 70 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,9 @@
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;
42+
import java.util.concurrent.atomic.AtomicBoolean;
4443
import java.util.regex.Pattern;
4544

4645
import org.eclipse.core.resources.IFile;
@@ -143,9 +142,10 @@ public class ProcessConsole extends IOConsole implements IConsole, IDebugEventSe
143142

144143
private volatile boolean fStreamsClosed;
145144
private volatile boolean disposed;
145+
private volatile boolean isVisible;
146146

147-
private final ScheduledExecutorService consoleNameUpdateExecutor;
148-
private volatile ScheduledFuture<?> pendingNameUpdate;
147+
private final ExecutorService consoleNameUpdateExecutor;
148+
private final AtomicBoolean updateRunning;
149149

150150
/**
151151
* Create process console with default encoding.
@@ -166,11 +166,12 @@ public ProcessConsole(IProcess process, IConsoleColorProvider colorProvider) {
166166
*/
167167
public ProcessConsole(IProcess process, IConsoleColorProvider colorProvider, String encoding) {
168168
super(IInternalDebugCoreConstants.EMPTY_STRING, IDebugUIConstants.ID_PROCESS_CONSOLE_TYPE, null, encoding, true);
169+
updateRunning = new AtomicBoolean();
169170
fStreamListeners = new ArrayList<>();
170171
fAllocateConsole = true;
171172
fProcess = process;
172173
fUserInput = getInputStream();
173-
consoleNameUpdateExecutor = Executors.newSingleThreadScheduledExecutor(r -> {
174+
consoleNameUpdateExecutor = Executors.newSingleThreadExecutor(r -> {
174175
Thread t = new Thread(r, "Console name updater"); //$NON-NLS-1$
175176
t.setDaemon(true);
176177
return t;
@@ -309,9 +310,14 @@ public IPageBookViewPage createPage(IConsoleView view) {
309310
/**
310311
* Computes and returns the current name of this console.
311312
*
313+
* @param triggerAsyncUpdate if <code>true</code> and process is still running,
314+
* triggers asynchronous update of console name every
315+
* second to update elapsed time display in console
316+
* name
317+
*
312318
* @return a name for this console
313319
*/
314-
protected String computeName() {
320+
protected String computeName(boolean triggerAsyncUpdate) {
315321
if (disposed) {
316322
return getName();
317323
}
@@ -337,7 +343,9 @@ protected String computeName() {
337343

338344
// Process is still running, so trigger async update of console name every
339345
// second to keep elapsed time updated.
340-
triggerAsyncConsoleNameUpdate();
346+
if (triggerAsyncUpdate) {
347+
triggerAsyncConsoleNameUpdate(label);
348+
}
341349
return label;
342350
}
343351

@@ -409,22 +417,38 @@ private static String computeProcessLabel(IProcess process, Date launchTime) {
409417
return procLabel;
410418
}
411419

412-
private void triggerAsyncConsoleNameUpdate() {
413-
if (disposed) {
420+
private void triggerAsyncConsoleNameUpdate(String newName) {
421+
if (!canUpdateConsoleName()) {
414422
return;
415423
}
424+
// update console name immediately to show elapsed time right after launch and
425+
// then start async update every second
426+
showName(false, newName);
416427

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;
424-
}
425-
resetName(false);
426-
}, 1, TimeUnit.SECONDS);
428+
if (!updateRunning.compareAndSet(false, true)) {
429+
return;
427430
}
431+
consoleNameUpdateExecutor.submit(() -> {
432+
try {
433+
while (!disposed) {
434+
try {
435+
Thread.sleep(1000);
436+
} catch (InterruptedException e) {
437+
// ignore and continue to update name
438+
}
439+
if (!canUpdateConsoleName()) {
440+
break;
441+
}
442+
showName(false, computeName(false));
443+
}
444+
} finally {
445+
updateRunning.set(false);
446+
}
447+
});
448+
}
449+
450+
private boolean canUpdateConsoleName() {
451+
return !disposed && isVisible && !fProcess.isTerminated();
428452
}
429453

430454
private static String computeElapsedTimeLabel(Date launchTime, Date terminateTime) {
@@ -567,14 +591,14 @@ public IProcess getProcess() {
567591
@Override
568592
protected void dispose() {
569593
super.dispose();
594+
disposed = true;
570595
consoleNameUpdateExecutor.shutdownNow();
571596
fColorProvider.disconnect();
572597
DebugPlugin.getDefault().removeDebugEventListener(this);
573598
DebugUIPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this);
574599
JFaceResources.getFontRegistry().removeListener(this);
575600
closeStreams();
576601
disposeStreams();
577-
disposed = true;
578602
}
579603

580604
/**
@@ -628,7 +652,7 @@ protected void init() {
628652
DebugPlugin.getDefault().addDebugEventListener(this);
629653
// computeName() after addDebugEventListener()
630654
// see https://github.com/eclipse-jdt/eclipse.jdt.debug/issues/390
631-
setName(computeName());
655+
setName(computeName(false));
632656
if (fProcess.isTerminated()) {
633657
closeStreams();
634658
resetName(true);
@@ -681,20 +705,25 @@ public void handleDebugEvents(DebugEvent[] events) {
681705
}
682706

683707
/**
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
708+
* Compute & update console name, notify listeners if content has changed.
709+
*
710+
* @param contentChanged whether the content of console has changed since last
711+
* name update.
687712
*/
688-
private synchronized void resetName(boolean changed) {
689-
final String newName = computeName();
713+
private synchronized void resetName(boolean contentChanged) {
714+
final String newName = computeName(false);
715+
showName(contentChanged, newName);
716+
}
717+
718+
private void showName(boolean contentChanged, final String newName) {
690719
String name = getName();
691720
if (!name.equals(newName)) {
692721
DebugUIPlugin.getStandardDisplay().execute(() -> {
693722
if (disposed) {
694723
return;
695724
}
696725
setName(newName);
697-
if (changed) {
726+
if (contentChanged) {
698727
warnOfContentChange();
699728
}
700729
});
@@ -1215,4 +1244,16 @@ public static String convertElapsedFormat(String humanReadable) {
12151244

12161245
return format.toString();
12171246
}
1218-
}
1247+
1248+
@Override
1249+
public void pageShown() {
1250+
isVisible = true;
1251+
computeName(true);
1252+
}
1253+
1254+
@Override
1255+
public void pageHidden() {
1256+
isVisible = false;
1257+
computeName(false);
1258+
}
1259+
}
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: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,4 +484,34 @@ public void refresh(final IConsole console) {
484484
redrawConsoleJob.schedule(50);
485485
}
486486

487+
@Override
488+
public void consoleShown(IConsole console) {
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+
}
487517
}

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+
getConsoleManager().consoleShown(recConsole);
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+
getConsoleManager().consoleShown(console);
841+
}
842+
}
825843
}
826844

827845
@Override

0 commit comments

Comments
 (0)