mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
Merge remote-tracking branch 'origin/patch'
Conflicts: Ghidra/Framework/Docking/src/main/java/ghidra/util/task/TaskDialog.java Ghidra/Framework/Docking/src/main/java/ghidra/util/task/TaskRunner.java Ghidra/Framework/Generic/src/main/java/ghidra/util/timer/GTimer.java
This commit is contained in:
commit
fa18866fa8
19 changed files with 296 additions and 278 deletions
|
@ -35,8 +35,7 @@ public class HexNumbersAction extends CompositeEditorTableAction implements Togg
|
|||
private boolean isSelected;
|
||||
|
||||
public HexNumbersAction(CompositeEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, PATH, PATH,
|
||||
null);
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, PATH, PATH, null);
|
||||
setDescription(DESCRIPTION);
|
||||
setEnabled(true);
|
||||
setSelected(model.isShowingNumbersInHex());
|
||||
|
@ -59,6 +58,9 @@ public class HexNumbersAction extends CompositeEditorTableAction implements Togg
|
|||
|
||||
@Override
|
||||
public void setSelected(boolean newValue) {
|
||||
if (isSelected == newValue) {
|
||||
return;
|
||||
}
|
||||
isSelected = newValue;
|
||||
firePropertyChanged(SELECTED_STATE_PROPERTY, !isSelected, isSelected);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,9 @@ import java.awt.Rectangle;
|
|||
import java.awt.event.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.*;
|
||||
|
@ -50,7 +52,6 @@ import ghidra.program.model.listing.Program;
|
|||
import ghidra.util.*;
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.table.GhidraTableFilterPanel;
|
||||
import ghidra.util.task.*;
|
||||
import resources.ResourceManager;
|
||||
|
@ -605,28 +606,34 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
void runScript(ResourceFile scriptFile, TaskListener listener) {
|
||||
if (SystemUtilities.isEventDispatchThread()) {
|
||||
new TaskLauncher(new Task("compiling script directory", true, false, true, true) {
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
doRunScript(scriptFile, listener);
|
||||
}
|
||||
}, plugin.getTool().getToolFrame(), 1000);
|
||||
}
|
||||
else {
|
||||
doRunScript(scriptFile, listener);
|
||||
}
|
||||
lastRunScript = scriptFile;
|
||||
GhidraScript script = doGetScriptInstance(scriptFile);
|
||||
doRunScript(script, listener);
|
||||
}
|
||||
|
||||
private void doRunScript(ResourceFile scriptFile, TaskListener listener) {
|
||||
lastRunScript = scriptFile;
|
||||
private GhidraScript doGetScriptInstance(ResourceFile scriptFile) {
|
||||
|
||||
ConsoleService console = plugin.getConsoleService();
|
||||
GhidraScript script = getScriptInstance(scriptFile, console);
|
||||
if (script == null) {
|
||||
return;
|
||||
Supplier<GhidraScript> scriptSupplier = () -> {
|
||||
ConsoleService console = plugin.getConsoleService();
|
||||
return getScriptInstance(scriptFile, console);
|
||||
};
|
||||
|
||||
if (!Swing.isSwingThread()) {
|
||||
return scriptSupplier.get();
|
||||
}
|
||||
|
||||
AtomicReference<GhidraScript> ref = new AtomicReference<>();
|
||||
TaskBuilder.withRunnable(monitor -> ref.set(scriptSupplier.get()))
|
||||
.setTitle("Compiling Script Directory")
|
||||
.setLaunchDelay(1000)
|
||||
.launchModal();
|
||||
|
||||
return ref.get();
|
||||
}
|
||||
|
||||
private void doRunScript(GhidraScript script, TaskListener listener) {
|
||||
|
||||
ConsoleService console = plugin.getConsoleService();
|
||||
RunScriptTask task = new RunScriptTask(script, plugin.getCurrentState(), console);
|
||||
runningScriptTaskSet.add(task);
|
||||
task.addTaskListener(listener);
|
||||
|
@ -811,13 +818,13 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||
/*
|
||||
Unusual Algorithm
|
||||
|
||||
The tree nodes represent categories, but do not contain nodes for individual
|
||||
The tree nodes represent categories, but do not contain nodes for individual
|
||||
scripts. We wish to remove any of the tree nodes that no longer represent script
|
||||
categories. (This can happen when a script is deleted or its category is changed.)
|
||||
This algorithm will assume that all nodes need to be deleted. Then, each script is
|
||||
examined, using its category to mark a given node as 'safe'; that node's parents are
|
||||
also marked as safe. Any nodes remaining unmarked have no reference script and
|
||||
will be deleted.
|
||||
also marked as safe. Any nodes remaining unmarked have no reference script and
|
||||
will be deleted.
|
||||
*/
|
||||
|
||||
// note: turn String[] to List<String> to use hashing
|
||||
|
|
|
@ -67,8 +67,10 @@ import utilities.util.FileUtilities;
|
|||
|
||||
public abstract class AbstractGhidraScriptMgrPluginTest
|
||||
extends AbstractGhidraHeadedIntegrationTest {
|
||||
protected static final int MAX_TIME = 4000;
|
||||
protected static final int SCRIPT_TIMEOUT_SECS = 5;
|
||||
// timeout for scripts run by invoking RunScriptTask directly
|
||||
protected static final int TASK_RUN_SCRIPT_TIMEOUT_SECS = 5;
|
||||
// timeout for scripts run indirectly through the GUI
|
||||
protected static final int GUI_RUN_SCRIPT_TIMEOUT_MSECS = 6 * DEFAULT_WAIT_TIMEOUT;
|
||||
protected TestEnv env;
|
||||
protected CodeBrowserPlugin browser;
|
||||
protected GhidraScriptMgrPlugin plugin;
|
||||
|
@ -594,12 +596,6 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||
waitForSwing();
|
||||
}
|
||||
|
||||
protected void performGlobalRunLastScriptAction() {
|
||||
// note: this action used to be different from the 'run last script'; currently they are
|
||||
// the same
|
||||
pressRunLastScriptButton();
|
||||
}
|
||||
|
||||
protected KeyBindingInputDialog pressKeyBindingAction() {
|
||||
DockingActionIf keyBindingAction = getAction(plugin, "Key Binding");
|
||||
performAction(keyBindingAction, false);
|
||||
|
@ -625,10 +621,17 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||
waitForSwing();
|
||||
}
|
||||
|
||||
protected String runScript(String scriptName) throws Exception {
|
||||
/**
|
||||
* Run the currently selected script by pressing the run button and return its output.
|
||||
*
|
||||
* @param taskName name for the task listener
|
||||
* @return script output written to the console
|
||||
* @throws Exception on failure, e.g. timeout
|
||||
*/
|
||||
protected String runSelectedScript(String taskName) throws Exception {
|
||||
clearConsole();
|
||||
|
||||
TaskListenerFlag taskFlag = new TaskListenerFlag(scriptName);
|
||||
TaskListenerFlag taskFlag = new TaskListenerFlag(taskName);
|
||||
TaskUtilities.addTrackedTaskListener(taskFlag);
|
||||
|
||||
pressRunButton();
|
||||
|
@ -639,8 +642,15 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||
return output;
|
||||
}
|
||||
|
||||
protected String runLastScript(String scriptName) throws Exception {
|
||||
TaskListenerFlag taskFlag = new TaskListenerFlag(scriptName);
|
||||
/**
|
||||
* Run the last script by pressing the last script button and return output.
|
||||
*
|
||||
* @param taskName name for the task listener
|
||||
* @return script output written to the console
|
||||
* @throws Exception on failure, e.g. timeout
|
||||
*/
|
||||
protected String runLastScript(String taskName) throws Exception {
|
||||
TaskListenerFlag taskFlag = new TaskListenerFlag(taskName);
|
||||
TaskUtilities.addTrackedTaskListener(taskFlag);
|
||||
|
||||
pressRunLastScriptButton();
|
||||
|
@ -651,18 +661,6 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||
return output;
|
||||
}
|
||||
|
||||
protected String runGlobalLastScriptAction(String scriptName) throws Exception {
|
||||
TaskListenerFlag taskFlag = new TaskListenerFlag(scriptName);
|
||||
TaskUtilities.addTrackedTaskListener(taskFlag);
|
||||
|
||||
performGlobalRunLastScriptAction();
|
||||
waitForTaskEnd(taskFlag);
|
||||
|
||||
String output = getConsoleText();
|
||||
clearConsole();
|
||||
return output;
|
||||
}
|
||||
|
||||
protected void deleteFile(ResourceFile file) {
|
||||
assertTrue(file.delete());
|
||||
}
|
||||
|
@ -1031,15 +1029,9 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||
protected void waitForTaskEnd(TaskListenerFlag flag) {
|
||||
waitForSwing();
|
||||
|
||||
int waitCount = 0;
|
||||
while (!flag.ended && waitCount < 401) {
|
||||
try {
|
||||
Thread.sleep(DEFAULT_WAIT_DELAY);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
// don't care; try again
|
||||
}
|
||||
waitCount++;
|
||||
int totalTime = 0;
|
||||
while (!flag.ended && totalTime <= GUI_RUN_SCRIPT_TIMEOUT_MSECS) {
|
||||
totalTime += sleep(DEFAULT_WAIT_DELAY);
|
||||
}
|
||||
|
||||
TaskUtilities.removeTrackedTaskListener(flag);
|
||||
|
@ -1047,6 +1039,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||
if (!flag.ended) {
|
||||
Assert.fail("Task took too long to complete: " + flag);
|
||||
}
|
||||
Msg.debug(this, flag.taskName + " task ended in " + totalTime + " ms");
|
||||
}
|
||||
|
||||
protected int getSelectedRow() {
|
||||
|
@ -1165,11 +1158,11 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||
pressButtonByText(window, "Cancel");
|
||||
}
|
||||
|
||||
protected TestChangeProgramScript startCancellableScript() throws Exception {
|
||||
protected TestChangeProgramScript startCancellableScriptTask() throws Exception {
|
||||
TestChangeProgramScript script = new TestChangeProgramScript();
|
||||
ResourceFile fakeFile = new ResourceFile(createTempFile(CANCELLABLE_SCRIPT_NAME, "java"));
|
||||
script.setSourceFile(fakeFile);
|
||||
runScript(script);
|
||||
startRunScriptTask(script);
|
||||
|
||||
boolean success = script.waitForStart();
|
||||
assertTrue("Test script did not get started!", success);
|
||||
|
@ -1202,7 +1195,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||
assertTrue("Timed-out waiting for cancelled script to complete", success);
|
||||
}
|
||||
|
||||
protected void runScript(GhidraScript script) throws Exception {
|
||||
protected void startRunScriptTask(GhidraScript script) throws Exception {
|
||||
Task task = new RunScriptTask(script, plugin.getCurrentState(), console);
|
||||
task.addTaskListener(provider.getTaskListener());
|
||||
new TaskLauncher(task, plugin.getTool().getToolFrame());
|
||||
|
@ -1213,10 +1206,10 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||
GhidraScript script =
|
||||
scriptProvider.getScriptInstance(scriptFile, new PrintWriter(System.err));
|
||||
|
||||
return runScriptAndGetOutput(script);
|
||||
return runScriptTaskAndGetOutput(script);
|
||||
}
|
||||
|
||||
protected String runScriptAndGetOutput(GhidraScript script) throws Exception {
|
||||
protected String runScriptTaskAndGetOutput(GhidraScript script) throws Exception {
|
||||
SpyConsole spyConsole = installSpyConsole();
|
||||
|
||||
Task task = new RunScriptTask(script, plugin.getCurrentState(), spyConsole);
|
||||
|
@ -1238,7 +1231,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||
|
||||
TaskLauncher.launch(task);
|
||||
|
||||
latch.await(SCRIPT_TIMEOUT_SECS, TimeUnit.SECONDS);
|
||||
latch.await(TASK_RUN_SCRIPT_TIMEOUT_SECS, TimeUnit.SECONDS);
|
||||
|
||||
String output = spyConsole.getApiOutput();
|
||||
spyConsole.clear();
|
||||
|
@ -1604,11 +1597,11 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||
}
|
||||
|
||||
boolean waitForStart() throws Exception {
|
||||
return startedLatch.await(SCRIPT_TIMEOUT_SECS, TimeUnit.SECONDS);
|
||||
return startedLatch.await(TASK_RUN_SCRIPT_TIMEOUT_SECS, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
boolean waitForFinish() throws Exception {
|
||||
return doneLatch.await(SCRIPT_TIMEOUT_SECS, TimeUnit.SECONDS);
|
||||
return doneLatch.await(TASK_RUN_SCRIPT_TIMEOUT_SECS, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -104,11 +104,11 @@ public class BundleStatusManagerTest extends AbstractGhidraScriptMgrPluginTest {
|
|||
assertTrue(status.isEnabled());
|
||||
assertScriptInTable(scriptFile);
|
||||
|
||||
runScript(SCRIPT_NAME);
|
||||
selectAndRunScript(SCRIPT_NAME);
|
||||
|
||||
cleanViaGUI(viewRow);
|
||||
|
||||
runScript(SCRIPT_NAME);
|
||||
runSelectedScript(SCRIPT_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -181,7 +181,7 @@ public class BundleStatusManagerTest extends AbstractGhidraScriptMgrPluginTest {
|
|||
|
||||
addBundlesViaGUI(dir1, dir2);
|
||||
|
||||
String output = runScript(TEST_SCRIPT_NAME + ".java");
|
||||
String output = selectAndRunScript(TEST_SCRIPT_NAME + ".java");
|
||||
assertEquals(EXPECTED_OUTPUT, output);
|
||||
|
||||
int row1 = getBundleRow(dir1);
|
||||
|
@ -392,11 +392,10 @@ public class BundleStatusManagerTest extends AbstractGhidraScriptMgrPluginTest {
|
|||
testBundleHostListener.awaitActivation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String runScript(String scriptName) throws Exception {
|
||||
public String selectAndRunScript(String scriptName) throws Exception {
|
||||
env.getTool().showComponentProvider(provider, true);
|
||||
selectScript(scriptName);
|
||||
String output = super.runScript(scriptName);
|
||||
String output = runSelectedScript(scriptName);
|
||||
env.getTool().showComponentProvider(bundleStatusProvider, true);
|
||||
return output;
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ public class GhidraScriptMgrPlugin1Test extends AbstractGhidraScriptMgrPluginTes
|
|||
//
|
||||
String initialScriptName = "HelloWorldScript.java";
|
||||
selectScript(initialScriptName);
|
||||
String fullOutput = runScript(initialScriptName);
|
||||
String fullOutput = runSelectedScript(initialScriptName);
|
||||
String expectedOutput = "Hello World";
|
||||
assertTrue("Script did not run - output: " + fullOutput,
|
||||
fullOutput.indexOf(expectedOutput) != -1);
|
||||
|
@ -63,7 +63,7 @@ public class GhidraScriptMgrPlugin1Test extends AbstractGhidraScriptMgrPluginTes
|
|||
//
|
||||
String secondScriptName = "FormatExampleScript.java";
|
||||
selectScript(secondScriptName);
|
||||
fullOutput = runScript(secondScriptName);
|
||||
fullOutput = runSelectedScript(secondScriptName);
|
||||
expectedOutput = "jumped over the";
|
||||
assertTrue("Script did not run - output: " + fullOutput,
|
||||
fullOutput.indexOf(expectedOutput) != -1);
|
||||
|
@ -84,7 +84,7 @@ public class GhidraScriptMgrPlugin1Test extends AbstractGhidraScriptMgrPluginTes
|
|||
//
|
||||
String scriptName = "HelloWorldScript.java";
|
||||
selectScript(scriptName);
|
||||
String fullOutput = runScript(scriptName);
|
||||
String fullOutput = runSelectedScript(scriptName);
|
||||
String expectedOutput = "Hello World";
|
||||
assertTrue("Script did not run - output: " + fullOutput,
|
||||
fullOutput.indexOf(expectedOutput) != -1);
|
||||
|
@ -105,7 +105,7 @@ public class GhidraScriptMgrPlugin1Test extends AbstractGhidraScriptMgrPluginTes
|
|||
//
|
||||
String scriptName = "HelloWorldScript.java";
|
||||
selectScript(scriptName);
|
||||
String fullOutput = runScript(scriptName);
|
||||
String fullOutput = runSelectedScript(scriptName);
|
||||
String expectedOutput = "Hello World";
|
||||
assertTrue("Script did not run - output: " + fullOutput,
|
||||
fullOutput.indexOf(expectedOutput) != -1);
|
||||
|
@ -115,7 +115,7 @@ public class GhidraScriptMgrPlugin1Test extends AbstractGhidraScriptMgrPluginTes
|
|||
//
|
||||
// Run the script again
|
||||
//
|
||||
fullOutput = runGlobalLastScriptAction(scriptName);
|
||||
fullOutput = runLastScript(scriptName);
|
||||
assertTrue("Did not rerun last run script", fullOutput.indexOf(expectedOutput) != -1);
|
||||
}
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ public class GhidraScriptMgrPlugin3Test extends AbstractGhidraScriptMgrPluginTes
|
|||
|
||||
pressSaveButton();
|
||||
|
||||
String scriptOutput = runScript(script.getName());
|
||||
String scriptOutput = runSelectedScript(script.getName());
|
||||
|
||||
assertTrue("Script output not generated",
|
||||
scriptOutput.contains("> new scripts are neato!"));
|
||||
|
@ -132,7 +132,7 @@ public class GhidraScriptMgrPlugin3Test extends AbstractGhidraScriptMgrPluginTes
|
|||
pressSaveButton();
|
||||
setTimestampToTheFuture(script);
|
||||
|
||||
String updatedScriptOutput = runScript(script.getName());
|
||||
String updatedScriptOutput = runSelectedScript(script.getName());
|
||||
|
||||
assertTrue("Script output not updated with new script contents - did recompile work?",
|
||||
StringUtilities.containsAll(updatedScriptOutput, "> new scripts are neato!",
|
||||
|
@ -530,7 +530,7 @@ public class GhidraScriptMgrPlugin3Test extends AbstractGhidraScriptMgrPluginTes
|
|||
|
||||
@Test
|
||||
public void testCancel() throws Exception {
|
||||
TestChangeProgramScript script = startCancellableScript();
|
||||
TestChangeProgramScript script = startCancellableScriptTask();
|
||||
|
||||
cancel();
|
||||
|
||||
|
@ -539,7 +539,7 @@ public class GhidraScriptMgrPlugin3Test extends AbstractGhidraScriptMgrPluginTes
|
|||
|
||||
@Test
|
||||
public void testCancel_DoNotCancel() throws Exception {
|
||||
TestChangeProgramScript script = startCancellableScript();
|
||||
TestChangeProgramScript script = startCancellableScriptTask();
|
||||
|
||||
cancel();
|
||||
|
||||
|
|
|
@ -1380,6 +1380,10 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
focusedPlaceholder.setSelected(false);
|
||||
}
|
||||
|
||||
// Activating placeholders is done to help users find widgets hiding in plain sight.
|
||||
// Assume that the user is no longer seeking a provider if they are clicking around.
|
||||
activatedInfo.clear();
|
||||
|
||||
focusedPlaceholder = placeholder;
|
||||
|
||||
// put the last focused placeholder at the front of the list for restoring focus work later
|
||||
|
@ -2294,5 +2298,10 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
}
|
||||
lastCalledTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
lastActivatedPlaceholder = null;
|
||||
lastCalledTimestamp = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -444,8 +444,7 @@ public abstract class DockingAction implements DockingActionIf {
|
|||
// menu path
|
||||
if (menuBarData != null) {
|
||||
buffer.append(" MENU PATH: ")
|
||||
.append(
|
||||
menuBarData.getMenuPathAsString());
|
||||
.append(menuBarData.getMenuPathAsString());
|
||||
buffer.append('\n');
|
||||
buffer.append(" MENU GROUP: ").append(menuBarData.getMenuGroup());
|
||||
buffer.append('\n');
|
||||
|
@ -468,8 +467,7 @@ public abstract class DockingAction implements DockingActionIf {
|
|||
// popup menu path
|
||||
if (popupMenuData != null) {
|
||||
buffer.append(" POPUP PATH: ")
|
||||
.append(
|
||||
popupMenuData.getMenuPathAsString());
|
||||
.append(popupMenuData.getMenuPathAsString());
|
||||
buffer.append('\n');
|
||||
buffer.append(" POPUP GROUP: ").append(popupMenuData.getMenuGroup());
|
||||
buffer.append('\n');
|
||||
|
@ -535,6 +533,9 @@ public abstract class DockingAction implements DockingActionIf {
|
|||
}
|
||||
|
||||
public void firePropertyChanged(String propertyName, Object oldValue, Object newValue) {
|
||||
if (Objects.equals(oldValue, newValue)) {
|
||||
return;
|
||||
}
|
||||
PropertyChangeEvent event = new PropertyChangeEvent(this, propertyName, oldValue, newValue);
|
||||
for (PropertyChangeListener listener : propertyListeners) {
|
||||
listener.propertyChange(event);
|
||||
|
|
|
@ -43,6 +43,9 @@ public abstract class ToggleDockingAction extends DockingAction implements Toggl
|
|||
|
||||
@Override
|
||||
public void setSelected(boolean newValue) {
|
||||
if (isSelected == newValue) {
|
||||
return;
|
||||
}
|
||||
isSelected = newValue;
|
||||
firePropertyChanged(SELECTED_STATE_PROPERTY, !isSelected, isSelected);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.awt.BorderLayout;
|
|||
import java.awt.Component;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
@ -64,7 +65,7 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
}
|
||||
};
|
||||
|
||||
private GTimerMonitor showTimer;
|
||||
private GTimerMonitor showTimer = GTimerMonitor.DUMMY;
|
||||
private CountDownLatch finished = new CountDownLatch(1);
|
||||
private boolean supportsProgress;
|
||||
|
||||
|
@ -78,17 +79,20 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
private SwingUpdateManager messageUpdater =
|
||||
new SwingUpdateManager(100, 250, () -> setStatusText(newMessage.getAndSet(null)));
|
||||
|
||||
private AtomicBoolean shown = new AtomicBoolean();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param centerOnComp component to be centered over when shown,
|
||||
* otherwise center over parent. If both centerOnComp and parent
|
||||
* are null, dialog will be centered on screen.
|
||||
* @param centerOnComp component to be centered over when shown, otherwise center over parent.
|
||||
* If both centerOnComp and parent are null, dialog will be centered on screen.
|
||||
* @param task the Task that this dialog will be associated with
|
||||
* @param finished overrides the latch used by this dialog to know when the task is finished
|
||||
*/
|
||||
public TaskDialog(Component centerOnComp, Task task) {
|
||||
TaskDialog(Component centerOnComp, Task task, CountDownLatch finished) {
|
||||
this(centerOnComp, task.getTaskTitle(), task.isModal(), task.canCancel(),
|
||||
task.hasProgress());
|
||||
this.finished = finished;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -209,14 +213,18 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
/**
|
||||
* Called after the task has been executed
|
||||
*/
|
||||
public synchronized void taskProcessed() {
|
||||
public void taskProcessed() {
|
||||
finished.countDown();
|
||||
monitorComponent.notifyChangeListeners();
|
||||
Swing.runLater(closeDialog);
|
||||
}
|
||||
|
||||
public synchronized boolean isCompleted() {
|
||||
return finished.getCount() == 0;
|
||||
/**
|
||||
* Returns true if this dialog's task has completed normally or been cancelled
|
||||
* @return true if this dialog's task has completed normally or been cancelled
|
||||
*/
|
||||
public boolean isCompleted() {
|
||||
return finished.getCount() == 0 || isCancelled();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -239,6 +247,14 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this dialog was ever made visible
|
||||
* @return true if shown
|
||||
*/
|
||||
public boolean wasShown() {
|
||||
return shown.get();
|
||||
}
|
||||
|
||||
private void doShowModal(int delay) {
|
||||
//
|
||||
// Note: we must block, since we are modal. Clients want us to finish the task before
|
||||
|
@ -272,6 +288,7 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
|
||||
Swing.runIfSwingOrRunLater(() -> {
|
||||
if (!isCompleted()) {
|
||||
shown.set(true);
|
||||
DockingWindowManager.showDialog(centerOnComponent, TaskDialog.this);
|
||||
}
|
||||
});
|
||||
|
@ -289,17 +306,9 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
}
|
||||
|
||||
public void dispose() {
|
||||
|
||||
cancel();
|
||||
showTimer.cancel();
|
||||
messageUpdater.dispose();
|
||||
|
||||
Runnable disposeTask = () -> {
|
||||
if (showTimer != null) {
|
||||
showTimer.cancel();
|
||||
showTimer = null;
|
||||
}
|
||||
};
|
||||
|
||||
Swing.runNow(disposeTask);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
|
|
|
@ -17,8 +17,6 @@ package ghidra.util.task;
|
|||
|
||||
import java.awt.Component;
|
||||
|
||||
import ghidra.util.Swing;
|
||||
|
||||
/**
|
||||
* Class to initiate a Task in a new Thread, and to show a progress dialog that indicates
|
||||
* activity <b>if the task takes too long</b>. The progress dialog will show an
|
||||
|
@ -229,20 +227,6 @@ public class TaskLauncher {
|
|||
return new TaskRunner(task, parent, delayMs, dialogWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the given task in the current thread, which <b>cannot be the Swing thread</b>
|
||||
*
|
||||
* @param task the task to run
|
||||
* @throws IllegalStateException if the given thread is the Swing thread
|
||||
*/
|
||||
protected void runInThisBackgroundThread(Task task) {
|
||||
if (Swing.isSwingThread()) {
|
||||
throw new IllegalStateException("Must not call this method from the Swing thread");
|
||||
}
|
||||
|
||||
task.monitoredRun(TaskMonitor.DUMMY);
|
||||
}
|
||||
|
||||
private static Component getParent(Component parent) {
|
||||
if (parent == null) {
|
||||
return null;
|
||||
|
|
|
@ -70,9 +70,21 @@ class TaskRunner {
|
|||
}
|
||||
|
||||
// protected to allow for dependency injection
|
||||
protected TaskDialog buildTaskDialog(Component comp, TaskMonitor monitor) {
|
||||
protected TaskDialog buildTaskDialog() {
|
||||
|
||||
TaskDialog dialog = createTaskDialog(comp);
|
||||
Component centerOverComponent = parent;
|
||||
Component currentParent = centerOverComponent;
|
||||
if (currentParent != null) {
|
||||
currentParent = WindowUtilities.windowForComponent(parent);
|
||||
}
|
||||
|
||||
if (currentParent == null) {
|
||||
centerOverComponent = null;
|
||||
}
|
||||
|
||||
// we pass in our 'finished' latch here to avoid relying on a Swing.runLater() callback
|
||||
// (see taskFinished())
|
||||
TaskDialog dialog = new TaskDialog(centerOverComponent, task, finished);
|
||||
dialog.setMinimumSize(dialogWidth, 0);
|
||||
dialog.setStatusJustification(task.getStatusTextAlignment());
|
||||
return dialog;
|
||||
|
@ -88,7 +100,6 @@ class TaskRunner {
|
|||
Executor executor = pool.getExecutor();
|
||||
executor.execute(() -> {
|
||||
Thread.currentThread().setName(name);
|
||||
|
||||
try {
|
||||
task.monitoredRun(monitor);
|
||||
}
|
||||
|
@ -98,33 +109,10 @@ class TaskRunner {
|
|||
});
|
||||
}
|
||||
|
||||
private TaskDialog createTaskDialog(Component comp) {
|
||||
Component centerOverComponent = comp;
|
||||
Component currentParent = centerOverComponent;
|
||||
if (currentParent != null) {
|
||||
currentParent = WindowUtilities.windowForComponent(comp);
|
||||
}
|
||||
|
||||
if (currentParent == null) {
|
||||
centerOverComponent = null;
|
||||
}
|
||||
|
||||
return new TaskDialog(centerOverComponent, task) {
|
||||
|
||||
// note: we override this method here to help with the race condition where the
|
||||
// TaskRunner does not yet know about the task dialog, but the background
|
||||
// thread has actually finished the work.
|
||||
@Override
|
||||
public synchronized boolean isCompleted() {
|
||||
return super.isCompleted() || isFinished();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void showTaskDialog(WrappingTaskMonitor monitor) {
|
||||
|
||||
Swing.runIfSwingOrRunLater(() -> {
|
||||
taskDialog = buildTaskDialog(parent, monitor);
|
||||
taskDialog = buildTaskDialog();
|
||||
monitor.setDelegate(taskDialog); // initialize the dialog to the current monitor state
|
||||
taskDialog.show(Math.max(delayMs, 0));
|
||||
});
|
||||
|
@ -135,6 +123,11 @@ class TaskRunner {
|
|||
}
|
||||
|
||||
private void taskFinished() {
|
||||
|
||||
// This will release the the task dialog. We passed this latch to the dialog at
|
||||
// construction so that does not block until we notify it in the Swing.runLater() below.
|
||||
// If we only rely on that notification, then the notification will be blocked when the
|
||||
// dialog is waiting in the Swing thread.
|
||||
finished.countDown();
|
||||
|
||||
// Do this later on the Swing thread to handle the race condition where the dialog
|
||||
|
|
|
@ -20,7 +20,6 @@ import static org.junit.Assert.*;
|
|||
import java.awt.Component;
|
||||
import java.util.Deque;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import docking.test.AbstractDockingTest;
|
||||
import ghidra.util.Swing;
|
||||
|
@ -37,8 +36,7 @@ public class AbstractTaskTest extends AbstractDockingTest {
|
|||
protected Deque<TDEvent> eventQueue = new LinkedBlockingDeque<>();
|
||||
|
||||
protected volatile TaskLauncherSpy taskLauncherSpy;
|
||||
protected volatile TaskDialogSpy dialogSpy;
|
||||
protected AtomicBoolean didRunInBackground = new AtomicBoolean();
|
||||
protected volatile TaskDialog taskDialog;
|
||||
|
||||
protected void assertDidNotRunInSwing() {
|
||||
for (TDEvent e : eventQueue) {
|
||||
|
@ -46,11 +44,9 @@ public class AbstractTaskTest extends AbstractDockingTest {
|
|||
}
|
||||
}
|
||||
|
||||
protected void assertRanInSwingThread() {
|
||||
assertFalse("Task was not run in the Swing thread", didRunInBackground.get());
|
||||
}
|
||||
|
||||
protected void assertSwingThreadBlockedForTask() {
|
||||
// if the last event is the swing thread, then we know the task blocked that thread, since
|
||||
// the task delay would have made it run last had it not blocked
|
||||
waitForSwing();
|
||||
TDEvent lastEvent = eventQueue.peekLast();
|
||||
boolean swingIsLast = lastEvent.getThreadName().contains("AWT");
|
||||
|
@ -70,26 +66,35 @@ public class AbstractTaskTest extends AbstractDockingTest {
|
|||
}
|
||||
|
||||
protected void assertNoDialogShown() {
|
||||
if (dialogSpy == null) {
|
||||
if (taskDialog == null) {
|
||||
return; // not shown
|
||||
}
|
||||
|
||||
assertFalse("Dialog should not have been shown.\nEvents: " + eventQueue,
|
||||
dialogSpy.wasShown());
|
||||
taskDialog.wasShown());
|
||||
}
|
||||
|
||||
protected void assertDialogShown() {
|
||||
assertTrue("Dialog should have been shown.\nEvents: " + eventQueue, dialogSpy.wasShown());
|
||||
assertTrue("Dialog should have been shown.\nEvents: " + eventQueue, taskDialog.wasShown());
|
||||
}
|
||||
|
||||
protected void waitForTask() throws Exception {
|
||||
threadsFinished.await(2, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches the task and waits until the dialog is shown
|
||||
* @param task the task to launch
|
||||
*/
|
||||
protected void launchTask(Task task) {
|
||||
launchTaskFromSwing(task);
|
||||
}
|
||||
|
||||
protected void launchTaskWithoutBlocking(Task task) {
|
||||
runSwing(() -> launchTaskFromSwing(task), false);
|
||||
waitFor(() -> taskDialog != null);
|
||||
}
|
||||
|
||||
protected void launchTaskFromSwing(Task task) {
|
||||
|
||||
runSwing(() -> {
|
||||
|
@ -99,14 +104,30 @@ public class AbstractTaskTest extends AbstractDockingTest {
|
|||
});
|
||||
}
|
||||
|
||||
protected void launchTaskFromSwing(FastModalTask task, int dialogDelay) {
|
||||
runSwing(() -> {
|
||||
taskLauncherSpy = new TaskLauncherSpy(task, dialogDelay);
|
||||
postEvent("After task launcher");
|
||||
threadsFinished.countDown();
|
||||
});
|
||||
}
|
||||
|
||||
protected void postEvent(String message) {
|
||||
eventQueue.add(new TDEvent(message));
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
protected class TaskLauncherSpy extends TaskLauncher {
|
||||
|
||||
public TaskLauncherSpy(Task task) {
|
||||
super(task, null, DELAY_LAUNCHER);
|
||||
this(task, DELAY_LAUNCHER);
|
||||
}
|
||||
|
||||
public TaskLauncherSpy(Task task, int dialogDelay) {
|
||||
super(task, null, dialogDelay);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -115,31 +136,12 @@ public class AbstractTaskTest extends AbstractDockingTest {
|
|||
|
||||
return new TaskRunner(task, parent, delay, dialogWidth) {
|
||||
@Override
|
||||
protected TaskDialog buildTaskDialog(Component comp, TaskMonitor monitor) {
|
||||
dialogSpy = new TaskDialogSpy(task) {
|
||||
@Override
|
||||
public synchronized boolean isCompleted() {
|
||||
return super.isCompleted() || isFinished();
|
||||
}
|
||||
};
|
||||
return dialogSpy;
|
||||
protected TaskDialog buildTaskDialog() {
|
||||
taskDialog = super.buildTaskDialog();
|
||||
return taskDialog;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runInThisBackgroundThread(Task task) {
|
||||
didRunInBackground.set(true);
|
||||
super.runInThisBackgroundThread(task);
|
||||
}
|
||||
|
||||
TaskDialogSpy getDialogSpy() {
|
||||
return dialogSpy;
|
||||
}
|
||||
|
||||
boolean didRunInBackground() {
|
||||
return didRunInBackground.get();
|
||||
}
|
||||
}
|
||||
|
||||
protected class FastModalTask extends Task {
|
||||
|
@ -203,6 +205,25 @@ public class AbstractTaskTest extends AbstractDockingTest {
|
|||
}
|
||||
}
|
||||
|
||||
protected class LatchedModalTask extends Task {
|
||||
|
||||
private CountDownLatch latch;
|
||||
|
||||
public LatchedModalTask(CountDownLatch latch) {
|
||||
super("Latched Modal Task", true, true, true);
|
||||
this.latch = latch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) {
|
||||
postEvent(getName() + " started...");
|
||||
waitFor(latch);
|
||||
sleep(DELAY_SLOW);
|
||||
threadsFinished.countDown();
|
||||
postEvent(getName() + " finished.");
|
||||
}
|
||||
}
|
||||
|
||||
protected class TDEvent {
|
||||
|
||||
protected String threadName = Thread.currentThread().getName();
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.util.task;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class TaskDialogSpy extends TaskDialog {
|
||||
private AtomicBoolean shown = new AtomicBoolean();
|
||||
|
||||
public TaskDialogSpy(Task task) {
|
||||
super(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doShow() {
|
||||
shown.set(true);
|
||||
super.doShow();
|
||||
}
|
||||
|
||||
boolean wasShown() {
|
||||
return shown.get();
|
||||
}
|
||||
}
|
|
@ -17,6 +17,8 @@ package ghidra.util.task;
|
|||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -51,7 +53,7 @@ public class TaskDialogTest extends AbstractTaskTest {
|
|||
|
||||
waitForTask();
|
||||
|
||||
assertFalse(dialogSpy.wasShown());
|
||||
assertFalse(taskDialog.wasShown());
|
||||
assertSwingThreadBlockedForTask();
|
||||
}
|
||||
|
||||
|
@ -76,7 +78,7 @@ public class TaskDialogTest extends AbstractTaskTest {
|
|||
|
||||
waitForTask();
|
||||
|
||||
assertFalse(dialogSpy.wasShown());
|
||||
assertFalse(taskDialog.wasShown());
|
||||
assertNoDialogShown();
|
||||
}
|
||||
|
||||
|
@ -98,16 +100,16 @@ public class TaskDialogTest extends AbstractTaskTest {
|
|||
*/
|
||||
@Test
|
||||
public void testTaskCancel() throws Exception {
|
||||
SlowModalTask task = new SlowModalTask();
|
||||
launchTask(task);
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
LatchedModalTask task = new LatchedModalTask(latch);
|
||||
launchTaskWithoutBlocking(task);
|
||||
|
||||
dialogSpy.doShow();
|
||||
taskDialog.doShow();
|
||||
latch.countDown();
|
||||
|
||||
waitForTask();
|
||||
|
||||
assertFalse(dialogSpy.isCancelled());
|
||||
dialogSpy.cancel();
|
||||
assertTrue(dialogSpy.isCancelled());
|
||||
assertFalse(taskDialog.isCancelled());
|
||||
taskDialog.cancel();
|
||||
assertTrue(taskDialog.isCancelled());
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -119,11 +121,11 @@ public class TaskDialogTest extends AbstractTaskTest {
|
|||
SlowModalTask task = new SlowModalTask();
|
||||
launchTask(task);
|
||||
|
||||
dialogSpy.doShow();
|
||||
dialogSpy.setCancelEnabled(false);
|
||||
taskDialog.doShow();
|
||||
taskDialog.setCancelEnabled(false);
|
||||
|
||||
waitForTask();
|
||||
|
||||
assertFalse(dialogSpy.isCancelEnabled());
|
||||
assertFalse(taskDialog.isCancelEnabled());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ public class TaskLauncherTest extends AbstractTaskTest {
|
|||
FastModalTask task = new FastModalTask();
|
||||
launchTaskFromBackground(task);
|
||||
waitForTask();
|
||||
assertRanInSwingThread();
|
||||
assertDidNotRunInSwing();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -93,11 +93,36 @@ public class TaskLauncherTest extends AbstractTaskTest {
|
|||
assertDidNotRunInSwing();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLaunchFromSwingThreadWithModalTaskDoesNotBlockForFullDelay() throws Exception {
|
||||
|
||||
//
|
||||
// Tests that a short-lived task does not block for the full dialog delay
|
||||
//
|
||||
|
||||
FastModalTask task = new FastModalTask();
|
||||
int dialogDelay = 3000;
|
||||
long start = System.nanoTime();
|
||||
launchTaskFromSwing(task, dialogDelay);
|
||||
waitForTask();
|
||||
long end = System.nanoTime();
|
||||
long totalTime = TimeUnit.NANOSECONDS.toMillis(end - start);
|
||||
|
||||
assertSwingThreadBlockedForTask();
|
||||
assertTrue(
|
||||
"Time waited is longer that the dialog delay: " + totalTime + " vs " + dialogDelay,
|
||||
totalTime < dialogDelay);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
private int getWaitTimeoutInSeconds() {
|
||||
return (int) TimeUnit.SECONDS.convert(DEFAULT_WAIT_TIMEOUT, TimeUnit.MILLISECONDS) * 2;
|
||||
}
|
||||
|
||||
protected void launchTaskFromBackground(Task task) throws InterruptedException {
|
||||
private void launchTaskFromBackground(Task task) throws InterruptedException {
|
||||
|
||||
CountDownLatch start = new CountDownLatch(1);
|
||||
new Thread("Test Task Launcher Background Client") {
|
||||
|
@ -114,7 +139,7 @@ public class TaskLauncherTest extends AbstractTaskTest {
|
|||
start.await(getWaitTimeoutInSeconds(), TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
protected void launchTaskFromTask() throws InterruptedException {
|
||||
private void launchTaskFromTask() throws InterruptedException {
|
||||
|
||||
TaskLaunchingTask task = new TaskLaunchingTask();
|
||||
|
||||
|
@ -133,6 +158,10 @@ public class TaskLauncherTest extends AbstractTaskTest {
|
|||
start.await(getWaitTimeoutInSeconds(), TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
private class TaskLaunchingTask extends Task {
|
||||
|
||||
public TaskLaunchingTask() {
|
||||
|
|
|
@ -27,7 +27,7 @@ import ghidra.util.Msg;
|
|||
*/
|
||||
public class GTimer {
|
||||
private static Timer timer;
|
||||
private static GTimerMonitor DO_NOTHING_MONITOR = new DoNothingMonitor();
|
||||
private static GTimerMonitor DO_NOTHING_MONITOR = GTimerMonitor.DUMMY;
|
||||
|
||||
/**
|
||||
* Schedules a runnable for execution after the specified delay. A delay value less than 0
|
||||
|
@ -77,24 +77,6 @@ public class GTimer {
|
|||
return timer;
|
||||
}
|
||||
|
||||
static class DoNothingMonitor implements GTimerMonitor {
|
||||
@Override
|
||||
public boolean cancel() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean didRun() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wasCancelled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class GTimerTask extends TimerTask implements GTimerMonitor {
|
||||
|
||||
private final Runnable runnable;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -18,9 +17,31 @@ package ghidra.util.timer;
|
|||
|
||||
/**
|
||||
* Monitor object returned from a GTimer.schedule() call
|
||||
*
|
||||
*/
|
||||
public interface GTimerMonitor {
|
||||
|
||||
/**
|
||||
* A dummy implementation of this interface
|
||||
*/
|
||||
public static GTimerMonitor DUMMY =
|
||||
new GTimerMonitor() {
|
||||
|
||||
@Override
|
||||
public boolean wasCancelled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean didRun() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancels the scheduled runnable associated with this GTimerMonitor if it has not already run.
|
||||
* @return true if the scheduled runnable was cancelled before it had a chance to execute.
|
||||
|
@ -35,7 +56,7 @@ public interface GTimerMonitor {
|
|||
|
||||
/**
|
||||
* Return true if the scheduled runnable was cancelled before it had a chance to run.
|
||||
* @return
|
||||
* @return true if the scheduled runnable was cancelled before it had a chance to run.
|
||||
*/
|
||||
public boolean wasCancelled();
|
||||
}
|
||||
|
|
|
@ -509,7 +509,6 @@ public class ToolTaskManager implements Runnable {
|
|||
|
||||
toolTaskMonitor.dispose();
|
||||
if (modalTaskDialog != null) {
|
||||
modalTaskDialog.cancel();
|
||||
modalTaskDialog.dispose();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue