Merge remote-tracking branch 'origin/master' into debugger

This commit is contained in:
Dan
2020-12-31 16:25:19 -05:00
8 changed files with 38 additions and 102 deletions

View File

@@ -100,8 +100,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
WeakDataStructureFactory.createSingleThreadAccessWeakSet(); WeakDataStructureFactory.createSingleThreadAccessWeakSet();
// The update should happen fairly quickly, as it is what rebuilds the window layout // The update should happen fairly quickly, as it is what rebuilds the window layout
private SwingUpdateManager rebuildUpdater = private SwingUpdateManager rebuildUpdater = new SwingUpdateManager(100, 750, this::doUpdate);
new SwingUpdateManager(100, 750, "Docking Windows Updater", this::doUpdate);
private boolean isVisible; private boolean isVisible;
private boolean isDocking; private boolean isDocking;
private boolean hasStatusBar; private boolean hasStatusBar;
@@ -820,16 +819,9 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
actionToGuiMapper.dispose(); actionToGuiMapper.dispose();
root.dispose(); root.dispose();
placeholderManager.disposePlaceholders(); placeholderManager.disposePlaceholders();
if (focusedPlaceholder != null) {
focusedPlaceholder.dispose();
}
if (nextFocusedPlaceholder != null) {
nextFocusedPlaceholder.dispose();
}
setNextFocusPlaceholder(null); setNextFocusPlaceholder(null);
removeInstance(this); removeInstance(this);
root = null; root = null;
@@ -1774,7 +1766,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
This method seeks to accomplish 2 goals: This method seeks to accomplish 2 goals:
1) find a suitable component over which to center, and 1) find a suitable component over which to center, and
2) ensure that the chosen component is in the parent hierarchy 2) ensure that the chosen component is in the parent hierarchy
*/ */
Component bestComponent = centeredOnComponent; Component bestComponent = centeredOnComponent;
if (SwingUtilities.isDescendingFrom(parent, bestComponent)) { if (SwingUtilities.isDescendingFrom(parent, bestComponent)) {
@@ -1808,7 +1800,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
/* /*
Note: Which window should be the parent of the dialog when the user does not specify? Note: Which window should be the parent of the dialog when the user does not specify?
Some use cases; a dialog is shown from: Some use cases; a dialog is shown from:
1) A toolbar action 1) A toolbar action
2) A component provider's code 2) A component provider's code
@@ -1816,7 +1808,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
4) A background thread 4) A background thread
5) The help window 5) The help window
6) A modal password dialog appears over the splash screen 6) A modal password dialog appears over the splash screen
It seems like the parent should be the active window for 1-2. It seems like the parent should be the active window for 1-2.
Case 3 should probably use the window of the dialog provider. Case 3 should probably use the window of the dialog provider.
Case 4 should probably use the main tool frame, since the user may be Case 4 should probably use the main tool frame, since the user may be
@@ -1824,12 +1816,12 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
active window, we can default to the tool's frame. active window, we can default to the tool's frame.
Case 5 should use the help window. Case 5 should use the help window.
Case 6 should use the splash screen as the parent. Case 6 should use the splash screen as the parent.
We have not yet solidified how we should parent. This documentation is meant to We have not yet solidified how we should parent. This documentation is meant to
move us towards clarity as we find Use Cases that don't make sense. (Once we move us towards clarity as we find Use Cases that don't make sense. (Once we
finalize our understanding, we should update the javadoc to list exactly where finalize our understanding, we should update the javadoc to list exactly where
the given Dialog Component will be shown.) the given Dialog Component will be shown.)
Use Case Use Case
A -The user presses an action on a toolbar from a window on screen 1, while the A -The user presses an action on a toolbar from a window on screen 1, while the
main tool frame is on screen 2. We want the popup window to appear on screen main tool frame is on screen 2. We want the popup window to appear on screen
@@ -1841,8 +1833,8 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
-modal - Java handles this correctly, allowing the new dialog to be used -modal - Java handles this correctly, allowing the new dialog to be used
-non-modal - Java prevents the non-modal from being editing if not parented -non-modal - Java prevents the non-modal from being editing if not parented
correctly correctly
For now, the easiest mental model to use is to always prefer the active window so For now, the easiest mental model to use is to always prefer the active window so
that a dialog will appear in the user's view. If we find a case where this is that a dialog will appear in the user's view. If we find a case where this is
not desired, then document it here. not desired, then document it here.

View File

@@ -15,6 +15,8 @@
*/ */
package docking; package docking;
import ghidra.util.Msg;
import java.awt.Dimension; import java.awt.Dimension;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -23,8 +25,6 @@ import javax.swing.*;
import org.jdom.Element; import org.jdom.Element;
import ghidra.util.Msg;
/** /**
* Node for managing a JSplitPane view of two component trees. * Node for managing a JSplitPane view of two component trees.
*/ */
@@ -213,17 +213,6 @@ class SplitNode extends Node {
String getDescription() { String getDescription() {
return "Split Node"; return "Split Node";
} }
@Override
void dispose() {
if (child1 != null) {
child1.dispose();
}
if (child2 != null) {
child2.dispose();
}
}
} }
//================================================================================================== //==================================================================================================

View File

@@ -35,7 +35,7 @@ public class WindowActionManager {
private ComponentPlaceholder placeHolderForScheduledActionUpdate; private ComponentPlaceholder placeHolderForScheduledActionUpdate;
private Runnable updateActionsRunnable = () -> processContextChanged(); private Runnable updateActionsRunnable = () -> processContextChanged();
private SwingUpdateManager updateManager = private SwingUpdateManager updateManager =
new SwingUpdateManager(500, 500, "Context Updater", updateActionsRunnable); new SwingUpdateManager(500, 500, "Context Update Manager", updateActionsRunnable);
public WindowActionManager(WindowNode node, MenuHandler menuBarHandler, public WindowActionManager(WindowNode node, MenuHandler menuBarHandler,
DockingWindowManager winMgr, MenuGroupMap menuGroupMap) { DockingWindowManager winMgr, MenuGroupMap menuGroupMap) {

View File

@@ -144,10 +144,6 @@ class ThreadedTableModelUpdateMgr<T> {
* is performed. It also is called if too many add/removes have been accumulated. * is performed. It also is called if too many add/removes have been accumulated.
*/ */
void reload() { void reload() {
if (isDisposed()) {
return;
}
synchronized (addRemoveUpdater) { synchronized (addRemoveUpdater) {
cancelAllJobs(); cancelAllJobs();
runJob(new LoadJob<>(model, monitor)); runJob(new LoadJob<>(model, monitor));
@@ -155,10 +151,6 @@ class ThreadedTableModelUpdateMgr<T> {
} }
void reloadSpecificData(List<T> data) { void reloadSpecificData(List<T> data) {
if (isDisposed()) {
return;
}
synchronized (addRemoveUpdater) { synchronized (addRemoveUpdater) {
cancelAllJobs(); cancelAllJobs();
TableData<T> tableData = TableData.createFullDataset(data); TableData<T> tableData = TableData.createFullDataset(data);
@@ -183,10 +175,6 @@ class ThreadedTableModelUpdateMgr<T> {
* to be re-sorted. * to be re-sorted.
*/ */
void sort(TableSortingContext<T> sortingContext, boolean forceSort) { void sort(TableSortingContext<T> sortingContext, boolean forceSort) {
if (isDisposed()) {
return;
}
synchronized (addRemoveUpdater) { synchronized (addRemoveUpdater) {
if (currentJob != null && pendingJob == null && if (currentJob != null && pendingJob == null &&
currentJob.requestSort(sortingContext, forceSort)) { currentJob.requestSort(sortingContext, forceSort)) {
@@ -215,10 +203,6 @@ class ThreadedTableModelUpdateMgr<T> {
* start a thread to do the work. * start a thread to do the work.
*/ */
void filter() { void filter() {
if (isDisposed()) {
return;
}
synchronized (addRemoveUpdater) { synchronized (addRemoveUpdater) {
if (currentJob != null && pendingJob == null && currentJob.requestFilter()) { if (currentJob != null && pendingJob == null && currentJob.requestFilter()) {
return; return;
@@ -242,10 +226,6 @@ class ThreadedTableModelUpdateMgr<T> {
* @param item the add/remove item to process. * @param item the add/remove item to process.
*/ */
void addRemove(AddRemoveListItem<T> item) { void addRemove(AddRemoveListItem<T> item) {
if (isDisposed()) {
return;
}
synchronized (addRemoveUpdater) { synchronized (addRemoveUpdater) {
if (pendingJob != null) { if (pendingJob != null) {
pendingJob.addRemove(item, getMaxAddRemoveCount()); pendingJob.addRemove(item, getMaxAddRemoveCount());
@@ -347,11 +327,6 @@ class ThreadedTableModelUpdateMgr<T> {
} }
} }
private boolean isDisposed() {
// the updater knows when it is disposed; use the updater to avoid keeping our own variable
return addRemoveUpdater.isDisposed();
}
/** /**
* Kicks the swing update manager to immediately process any accumulated add/removes. * Kicks the swing update manager to immediately process any accumulated add/removes.
*/ */

View File

@@ -1113,7 +1113,7 @@ public abstract class AbstractGenericTest extends AbstractGTest {
* *
* @param r the runnable code snippet * @param r the runnable code snippet
*/ */
public static void runSwingLater(Runnable r) { public void runSwingLater(Runnable r) {
runSwing(r, false); runSwing(r, false);
} }
@@ -1603,34 +1603,23 @@ public abstract class AbstractGenericTest extends AbstractGTest {
(WeakSet<SwingUpdateManager>) getInstanceField("instances", (WeakSet<SwingUpdateManager>) getInstanceField("instances",
SwingUpdateManager.class); SwingUpdateManager.class);
for (AbstractSwingUpdateManager manager : s) { for (AbstractSwingUpdateManager manager : s) {
// Ignore update managers that fire often and are unlikely to affect tests.
// (Hardcoding this is brittle, but these update managers live in lower packages
// and a larger refactoring didn't seem worth the cost at this time.)
String name = manager.getName();
if (name.equals("Context Updater") ||
name.equals("Docking Windows Updater")) {
continue;
}
set.add(manager); set.add(manager);
} }
}); });
/* /*
// performance debug
long start = System.nanoTime(); long start = System.nanoTime();
boolean wasEverBusy = waitForSwing(set); boolean wasEverBusy = waitForSwing(set, true);
long end = System.nanoTime(); long end = System.nanoTime();
Msg.out("\twaitForSwing() - " + Msg.out("\twaitForSwing() - " +
TimeUnit.MILLISECONDS.convert(end - start, TimeUnit.NANOSECONDS)); TimeUnit.MILLISECONDS.convert(end - start, TimeUnit.NANOSECONDS));
*/ */
boolean wasEverBusy = waitForSwing(set); boolean wasEverBusy = waitForSwing(set, true);
return wasEverBusy; return wasEverBusy;
} }
private static boolean waitForSwing(Set<AbstractSwingUpdateManager> managers) { private static boolean waitForSwing(Set<AbstractSwingUpdateManager> managers, boolean flush) {
// Note: not sure how long is too long to wait for the Swing thread and update managers // Note: not sure how long is too long to wait for the Swing thread and update managers
// to finish. This is usually less than a second. We have seen a degenerate // to finish. This is usually less than a second. We have seen a degenerate
@@ -1640,7 +1629,7 @@ public abstract class AbstractGenericTest extends AbstractGTest {
int totalTime = 0; int totalTime = 0;
// flush all managers up front to get them started before we check them // flush all managers up front to get them started before we check them
flushAllManagers(managers); flushAllManagers(managers, flush);
boolean wasEverBusy = false; boolean wasEverBusy = false;
boolean keepGoing = true; boolean keepGoing = true;
@@ -1659,13 +1648,14 @@ public abstract class AbstractGenericTest extends AbstractGTest {
// Msg.out("busy manager: " + manager.toStringDebug()); // Msg.out("busy manager: " + manager.toStringDebug());
doFlush(manager); doFlush(flush, manager);
keepGoing = true; // true, since we had a busy signal
wasEverBusy = true;
boolean isBusy = true; boolean isBusy = true;
while (isBusy) { while (isBusy) {
keepGoing = true; // true, since we had a busy signal
wasEverBusy = true;
totalTime += sleep(DEFAULT_WAIT_DELAY); totalTime += sleep(DEFAULT_WAIT_DELAY);
if (totalTime >= MAX_SWING_TIMEOUT) { if (totalTime >= MAX_SWING_TIMEOUT) {
// eject! // eject!
@@ -1686,7 +1676,7 @@ public abstract class AbstractGenericTest extends AbstractGTest {
return wasEverBusy; return wasEverBusy;
} }
private static void flushAllManagers(Set<AbstractSwingUpdateManager> managers) { private static void flushAllManagers(Set<AbstractSwingUpdateManager> managers, boolean flush) {
// //
// Some update managers will make an update that causes another manager to schedule an // Some update managers will make an update that causes another manager to schedule an
@@ -1703,26 +1693,21 @@ public abstract class AbstractGenericTest extends AbstractGTest {
// which would be 2 // which would be 2
int n = 3; int n = 3;
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
boolean didFlush = false;
for (AbstractSwingUpdateManager manager : managers) { for (AbstractSwingUpdateManager manager : managers) {
didFlush = doFlush(manager); doFlush(flush, manager);
}
if (!didFlush) {
return; // skip extra waiting when no work was done
} }
} }
} }
private static boolean doFlush(AbstractSwingUpdateManager manager) { private static void doFlush(boolean doFlush, AbstractSwingUpdateManager manager) {
if (!doFlush) {
if (!manager.hasPendingUpdates()) { return;
return false; // skip the yield call, as it consumes time
} }
runSwingLater(() -> manager.flush()); runSwing(() -> {
manager.flush();
}, false);
yieldToSwing(); yieldToSwing();
return true;
} }
/** /**

View File

@@ -56,6 +56,7 @@ public abstract class AbstractSwingUpdateManager {
public static final int DEFAULT_MAX_DELAY = 30000; public static final int DEFAULT_MAX_DELAY = 30000;
protected static final int MIN_DELAY_FLOOR = 10; protected static final int MIN_DELAY_FLOOR = 10;
protected static final int DEFAULT_MIN_DELAY = 250; protected static final int DEFAULT_MIN_DELAY = 250;
protected static final String DEFAULT_NAME = AbstractSwingUpdateManager.class.getSimpleName();
private static final WeakSet<AbstractSwingUpdateManager> instances = private static final WeakSet<AbstractSwingUpdateManager> instances =
WeakDataStructureFactory.createCopyOnReadWeakSet(); WeakDataStructureFactory.createCopyOnReadWeakSet();
@@ -106,7 +107,7 @@ public abstract class AbstractSwingUpdateManager {
* @param maxDelay the maximum amount of time to wait between gui updates. * @param maxDelay the maximum amount of time to wait between gui updates.
*/ */
protected AbstractSwingUpdateManager(int minDelay, int maxDelay) { protected AbstractSwingUpdateManager(int minDelay, int maxDelay) {
this(minDelay, maxDelay, null); this(minDelay, maxDelay, DEFAULT_NAME);
} }
/** /**
@@ -123,7 +124,7 @@ public abstract class AbstractSwingUpdateManager {
protected AbstractSwingUpdateManager(int minDelay, int maxDelay, String name) { protected AbstractSwingUpdateManager(int minDelay, int maxDelay, String name) {
this.maxDelay = maxDelay; this.maxDelay = maxDelay;
this.name = name != null ? name : getClass().getSimpleName(); this.name = name;
recordInception(); recordInception();
this.minDelay = Math.max(MIN_DELAY_FLOOR, minDelay); this.minDelay = Math.max(MIN_DELAY_FLOOR, minDelay);
@@ -182,15 +183,6 @@ public abstract class AbstractSwingUpdateManager {
Swing.runNow(this::checkForWork); Swing.runNow(this::checkForWork);
} }
/**
* Returns the name given to this update manager. If no name was provided at construction,
* then the class name is used.
* @return the name
*/
public String getName() {
return name;
}
/** /**
* Causes this run manager to run if it has a pending update * Causes this run manager to run if it has a pending update
*/ */
@@ -371,10 +363,12 @@ public abstract class AbstractSwingUpdateManager {
StackTraceElement[] trace = t.getStackTrace(); StackTraceElement[] trace = t.getStackTrace();
String classInfo = trace[0].toString(); String classInfo = trace[0].toString();
/*
// debug source of creation // debug source of creation
Throwable filtered = ReflectionUtilities.filterJavaThrowable(t); Throwable filtered = ReflectionUtilities.filterJavaThrowable(t);
String string = ReflectionUtilities.stackTraceToString(filtered); String string = ReflectionUtilities.stackTraceToString(filtered);
classInfo = classInfo + "\n\tfrom:\n\n" + string; classInfo = classInfo + "\n\tfrom:\n\n" + string;
*/
return classInfo; return classInfo;
} }

View File

@@ -36,11 +36,11 @@ public class BufferedSwingRunner extends AbstractSwingUpdateManager {
* @param maxDelay the maximum amount of time to wait between gui updates. * @param maxDelay the maximum amount of time to wait between gui updates.
*/ */
public BufferedSwingRunner(int minDelay, int maxDelay) { public BufferedSwingRunner(int minDelay, int maxDelay) {
super(minDelay, maxDelay, null); super(minDelay, maxDelay, DEFAULT_NAME);
} }
public BufferedSwingRunner() { public BufferedSwingRunner() {
super(DEFAULT_MIN_DELAY, DEFAULT_MAX_DELAY, null); super(DEFAULT_MIN_DELAY, DEFAULT_MAX_DELAY, DEFAULT_NAME);
} }
@Override @Override

View File

@@ -82,7 +82,8 @@ public class SwingUpdateManager extends AbstractSwingUpdateManager {
* @param r the runnable that performs the client work. * @param r the runnable that performs the client work.
*/ */
public SwingUpdateManager(int minDelay, int maxDelay, Runnable r) { public SwingUpdateManager(int minDelay, int maxDelay, Runnable r) {
this(minDelay, maxDelay, null, r); super(minDelay, maxDelay, DEFAULT_NAME);
this.clientRunnable = r;
} }
/** /**