From b8f33291eea59369a38f22ed6207ed5fe134827f Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Thu, 21 Oct 2021 16:32:21 -0400 Subject: [PATCH] Switched help validation away from using a dialog; updated the thread pool class to allow clients to easily run code in a background thread --- .../main/java/docking/help/HelpManager.java | 59 +++++++++---------- .../java/generic/concurrent/GThreadPool.java | 30 +++++++--- 2 files changed, 50 insertions(+), 39 deletions(-) diff --git a/Ghidra/Framework/Docking/src/main/java/docking/help/HelpManager.java b/Ghidra/Framework/Docking/src/main/java/docking/help/HelpManager.java index d20ca7ec32..24318f17be 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/help/HelpManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/help/HelpManager.java @@ -32,20 +32,20 @@ import javax.swing.UIManager; import docking.ComponentProvider; import docking.action.DockingActionIf; +import generic.concurrent.GThreadPool; import generic.util.WindowUtilities; import ghidra.util.*; import ghidra.util.exception.AssertException; import ghidra.util.exception.CancelledException; -import ghidra.util.task.TaskLauncher; import ghidra.util.task.TaskMonitor; import resources.ResourceManager; import utilities.util.reflection.ReflectionUtilities; /** * Class that uses JavaHelp browser to show context sensitive help. - * + * *

Note: this manager will validate all registered help when in development mode. In order - * to catch items that have not registered help at all, we rely on those items to register a + * to catch items that have not registered help at all, we rely on those items to register a * default {@link HelpLocation} that will get flagged as invalid. Examples of this usage are * the {@link DockingActionIf} and the {@link ComponentProvider} base classes. */ @@ -78,7 +78,7 @@ public class HelpManager implements HelpService { /** * Constructor. - * + * * @param url url for the main HelpSet file for the application. * @throws HelpSetException if HelpSet could not be created */ @@ -104,7 +104,7 @@ public class HelpManager implements HelpService { /** * Add the help set for the given URL. - * + * * @param url url for the HelpSet (.hs) file * @param classLoader the help classloader that knows how to find help modules in the classpath * @throws HelpSetException if the help set could not be created from the given URL. @@ -160,8 +160,8 @@ public class HelpManager implements HelpService { } // Implementation Note: the same object can have different help registered. For example, - // DockingActions do this as part of their construction process. - // These actions will register a default help location and then + // DockingActions do this as part of their construction process. + // These actions will register a default help location and then // subclasses may change that location. We always use the most // recently registered action. helpLocations.put(helpObject, location); @@ -188,6 +188,7 @@ public class HelpManager implements HelpService { /** * Returns the master help set (the one into which all other help sets are merged). + * @return the help set */ public GHelpSet getMasterHelpSet() { return mainHS; @@ -195,10 +196,10 @@ public class HelpManager implements HelpService { /** * Display the help page for the given URL. This is a specialty method for displaying - * help when a specific file is desired, like an introduction page. Showing help for - * objects within the system is accomplished by calling + * help when a specific file is desired, like an introduction page. Showing help for + * objects within the system is accomplished by calling * {@link #showHelp(Object, boolean, Component)}. - * + * * @param url the URL to display * @see #showHelp(Object, boolean, Component) */ @@ -400,7 +401,7 @@ public class HelpManager implements HelpService { ((DefaultHelpBroker) mainHB).setActivationWindow(owner); } - // make sure we are visible before we set data (prevents bugs in JavaHelp) + // make sure we are visible before we set data (prevents bugs in JavaHelp) mainHB.setDisplayed(true); if (!wasDisplayed) { @@ -443,21 +444,20 @@ public class HelpManager implements HelpService { return; } - TaskLauncher.launchNonModal("Validating Help", monitor -> { - try { - printBadHelp(monitor); - } - catch (CancelledException e) { - // user cancelled; just exit - } - }); + GThreadPool.runAsync(Swing.GSWING_THREAD_POOL_NAME, this::doPrintBadHelp); } - private void printBadHelp(TaskMonitor monitor) throws CancelledException { + private void doPrintBadHelp() { - Map badHelp = getInvalidHelpLocations(monitor); - if (badHelp.isEmpty()) { - return; + Map badHelp; + try { + badHelp = getInvalidHelpLocations(TaskMonitor.DUMMY); + if (badHelp.isEmpty()) { + return; + } + } + catch (CancelledException e) { + return; // user cancelled } StringBuilder buffy = new StringBuilder(); @@ -474,7 +474,6 @@ public class HelpManager implements HelpService { throws CancelledException { Map map = new WeakHashMap<>(); - Map helpLocationsCopy = copyHelpLocations(); monitor.initialize(helpLocationsCopy.size()); Set> entries = helpLocationsCopy.entrySet(); @@ -493,7 +492,7 @@ public class HelpManager implements HelpService { } private Map copyHelpLocations() { - // we must copy the help locations, since we are in a background thread and the + // we must copy the help locations, since we are in a background thread and the // locations map is frequently updated by the Swing thread return Swing.runNow(() -> new HashMap<>(helpLocations)); } @@ -501,9 +500,9 @@ public class HelpManager implements HelpService { // // Warning! // This code has timing implications. DockingActions register themselves with the help - // system as part of their construction. At that point, they are not usually fully + // system as part of their construction. At that point, they are not usually fully // constructed, as most clients will use the newly constructed action to set the various - // toolbar/menu/popup data elements. For us to know if the action is really only for + // toolbar/menu/popup data elements. For us to know if the action is really only for // keybinding purposes, we have to do this check after the action is fully constructed. // private boolean isKeybindingOnly(Object helpee) { @@ -630,7 +629,7 @@ public class HelpManager implements HelpService { } // Note: not sure if we ever need to merge again after the initial load. If so, then - // this flag doesn't make sense. However, as of this writing, we do not discover + // this flag doesn't make sense. However, as of this writing, we do not discover // new help sets on the fly. hasMergedHelpSets = true; helpSetsPendingMerge.clear(); @@ -657,7 +656,7 @@ public class HelpManager implements HelpService { /** * Create a new help set for the given url, if one does * not already exist. - * @param classLoader + * @param classLoader the class loader */ private HelpSet createHelpSet(URL url, GHelpClassLoader classLoader) throws HelpSetException { if (!urlToHelpSets.containsKey(url)) { @@ -668,7 +667,7 @@ public class HelpManager implements HelpService { return null; } - /** + /** * Set the color resources on the JEditorPane for selection so that * you can see the highlights when you do a search in the JavaHelp. */ diff --git a/Ghidra/Framework/Generic/src/main/java/generic/concurrent/GThreadPool.java b/Ghidra/Framework/Generic/src/main/java/generic/concurrent/GThreadPool.java index eefb4f0276..b900b6c61c 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/concurrent/GThreadPool.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/concurrent/GThreadPool.java @@ -31,17 +31,17 @@ import ghidra.util.SystemUtilities; *

* The simple behavior for when new tasks are submitted:
* 1) If there any idle threads, use that thread.
- * 2) If all existing threads are busy and the number of threads is less than max threads, add a + * 2) If all existing threads are busy and the number of threads is less than max threads, add a * new thread and use it.
* 3) if all threads are busy and there are max number of threads, queue the item until a thread * becomes free.
*

* The simple behavior for when tasks are completed by a thread:
* 1) If there are tasks in the queue, start processing a new item in the newly freed thread.
- * 2) if there are more threads that min threads, allow this thread to die if no new + * 2) if there are more threads that min threads, allow this thread to die if no new * jobs arrive before * the "KEEP ALIVE" time expires which is currently 15 seconds.
- * 3) if there are min threads or less, allow this thread to wait forever for a new job + * 3) if there are min threads or less, allow this thread to wait forever for a new job * to arrive.
*/ public class GThreadPool { @@ -76,6 +76,18 @@ public class GThreadPool { return threadPool; } + /** + * Runs the given runnable in a background thread using a shared thread pool of the given name. + * @param poolName the thread pool name + * @param r the runnable + * @return the future + */ + public static CompletableFuture runAsync(String poolName, Runnable r) { + GThreadPool pool = getSharedThreadPool(poolName); + Executor ex = pool.getExecutor(); + return CompletableFuture.runAsync(r, ex); + } + private GThreadPool(String name) { this.name = name; executor = new GThreadPoolExecutor(); @@ -157,7 +169,7 @@ public class GThreadPool { /** * Returns true if this is not a shared thread pool. - * + * * @return true if this is not a shared thread pool. */ public boolean isPrivate() { @@ -166,12 +178,12 @@ public class GThreadPool { /** * Returns the {@link Executor} used by this thread pool. - * - *

Note: normal usage of this thread pool contraindicates accessing the executor of + * + *

Note: normal usage of this thread pool contraindicates accessing the executor of * this pool. For managing your own jobs, you should use the method on this class directly. - * The intent of this method is to provide access to the executor so that it may be + * The intent of this method is to provide access to the executor so that it may be * passed to other asynchronous APIs, such as the {@link CompletableFuture}. - * + * * @return the executor */ public Executor getExecutor() { @@ -180,7 +192,7 @@ public class GThreadPool { //================================================================================================== // Inner Classes -//================================================================================================== +//================================================================================================== private class GThreadPoolExecutor extends ThreadPoolExecutor { private volatile int maxThreadCount = SystemUtilities.getDefaultThreadPoolSize();