Merge branch 'dragonmacher-task-launcher-backgrounding-update'

This commit is contained in:
dragonmacher 2019-05-23 18:26:53 -04:00
commit 8cdfe79def
28 changed files with 605 additions and 318 deletions

View file

@ -32,8 +32,9 @@ public class MoveBlockTask extends ProgramTask {
private Address currentStart; private Address currentStart;
private Address newStart; private Address newStart;
private MoveBlockListener listener; private MoveBlockListener listener;
private boolean wasCancelled; private boolean cancelled;
private boolean status; private String statusMessage;
private boolean success;
/** /**
* Creates a background command for moving memory blocks. The memory block * Creates a background command for moving memory blocks. The memory block
@ -62,58 +63,53 @@ public class MoveBlockTask extends ProgramTask {
Memory mem = program.getMemory(); Memory mem = program.getMemory();
MemoryBlock block = mem.getBlock(currentStart); MemoryBlock block = mem.getBlock(currentStart);
monitor.setMessage("Moving Memory Block ..."); monitor.setMessage("Moving Memory Block ...");
String msg = ""; statusMessage = "";
Throwable cause = null; Throwable cause = null;
try { try {
mem.moveBlock(block, newStart, monitor); mem.moveBlock(block, newStart, monitor);
if (monitor.isCancelled()) { if (monitor.isCancelled()) {
wasCancelled = true; cancelled = true;
} }
else { else {
status = true; success = true;
listener.moveBlockCompleted(this); listener.moveBlockCompleted(this);
return; return;
} }
} }
catch (OutOfMemoryError e) { catch (OutOfMemoryError e) {
msg = "Insufficient memory to complete operation"; statusMessage = "Insufficient memory to complete operation";
cause = e; cause = e;
} }
catch (NotFoundException exc) { catch (NotFoundException e) {
msg = "Memory block not found"; statusMessage = "Memory block not found";
cause = exc; cause = e;
} }
catch (MemoryConflictException exc) { catch (MemoryConflictException | MemoryBlockException | IllegalArgumentException e) {
msg = exc.getMessage(); statusMessage = e.getMessage();
cause = exc;
}
catch (MemoryBlockException exc) {
msg = exc.getMessage();
cause = exc;
}
catch (IllegalArgumentException e) {
msg = e.getMessage();
cause = e; cause = e;
} }
catch (Throwable t) { catch (Throwable t) {
Msg.error(this, "Unexpected Exception: " + t.getMessage(), t); Msg.error(this, "Unexpected Exception: " + t.getMessage(), t);
msg = t.getMessage(); statusMessage = t.getMessage();
if (msg == null) { if (statusMessage == null) {
msg = t.toString(); statusMessage = t.toString();
} }
cause = t; cause = t;
} }
monitor.setMessage(msg);
listener.moveBlockCompleted(this); listener.moveBlockCompleted(this);
throw new RollbackException(msg, cause); throw new RollbackException(statusMessage, cause);
} }
public boolean isCancelled() { public boolean isCancelled() {
return wasCancelled; return cancelled;
} }
public boolean getStatus() { public boolean wasSuccessful() {
return status; return success;
}
public String getStatusMessage() {
return statusMessage;
} }
} }

View file

@ -1467,12 +1467,22 @@ public class AutoAnalysisManager implements DomainObjectListener, DomainObjectCl
// ignore // ignore
} }
@Override
public boolean isIndeterminate() {
return false;
}
@Override @Override
public void setMessage(String message) { public void setMessage(String message) {
dominantMonitor.setMessage(message); dominantMonitor.setMessage(message);
slaveMonitor.setMessage(message); slaveMonitor.setMessage(message);
} }
@Override
public String getMessage() {
return dominantMonitor.getMessage();
}
@Override @Override
public void setProgress(long value) { public void setProgress(long value) {
dominantMonitor.setProgress(value); dominantMonitor.setProgress(value);

View file

@ -29,9 +29,9 @@ import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFactory; import ghidra.program.model.address.AddressFactory;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import ghidra.util.Swing;
import ghidra.util.layout.PairLayout; import ghidra.util.layout.PairLayout;
import ghidra.util.task.BackgroundThreadTaskLauncher; import ghidra.util.task.TaskBuilder;
import ghidra.util.task.TaskMonitorAdapter;
/** /**
* Dialog that uses a model to validate the fields for moving a block of memory. * Dialog that uses a model to validate the fields for moving a block of memory.
@ -49,15 +49,6 @@ public class MoveBlockDialog extends DialogComponentProvider implements MoveBloc
private MoveBlockModel model; private MoveBlockModel model;
private PluginTool tool; private PluginTool tool;
/**
* Constructor for MoveBlockDialog.
*
* @param dialog
* @param title
* @param modal
* @param includeStatus
* @param includeButtons
*/
MoveBlockDialog(MoveBlockModel model, PluginTool tool) { MoveBlockDialog(MoveBlockModel model, PluginTool tool) {
super("Move Memory Block"); super("Move Memory Block");
this.model = model; this.model = model;
@ -69,33 +60,22 @@ public class MoveBlockDialog extends DialogComponentProvider implements MoveBloc
addCancelButton(); addCancelButton();
} }
/**
* @see ghidra.app.plugin.contrib.memory.MoveBlockListener#moveBlockCompleted(boolean,
* java.lang.String)
*/
@Override @Override
public void moveBlockCompleted(final MoveBlockTask cmd) { public void moveBlockCompleted(MoveBlockTask task) {
Runnable r = () -> {
if (cmd.getStatus()) {
close();
model.dispose();
}
else {
setCursor(Cursor.getDefaultCursor()); setCursor(Cursor.getDefaultCursor());
setOkEnabled(false); boolean success = task.wasSuccessful();
if (cmd.isCancelled()) { setOkEnabled(success);
tool.setStatusInfo(getStatusText()); setStatusText(task.getStatusMessage());
Swing.runLater(() -> {
if (success) {
close(); close();
model.dispose(); model.dispose();
} }
} });
};
SwingUtilities.invokeLater(r);
} }
/**
* @see ghidra.app.plugin.contrib.memory.MoveBlockListener#stateChanged()
*/
@Override @Override
public void stateChanged() { public void stateChanged() {
setOkEnabled(false); setOkEnabled(false);
@ -138,18 +118,20 @@ public class MoveBlockDialog extends DialogComponentProvider implements MoveBloc
setOkEnabled(false); setOkEnabled(false);
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
BackgroundThreadTaskLauncher launcher = new BackgroundThreadTaskLauncher(model.makeTask()); MoveBlockTask task = model.makeTask();
launcher.run(new TaskMonitorAdapter() {
@Override //@formatter:off
public void setMessage(String message) { TaskBuilder.withTask(task)
setStatusText(message); .setParent(this.getComponent())
} .launchModal()
}); ;
//@formatter:on
} }
@Override @Override
protected void cancelCallback() { protected void cancelCallback() {
close(); close();
model.dispose();
} }
private JPanel buildMainPanel() { private JPanel buildMainPanel() {

View file

@ -17,15 +17,10 @@ package ghidra.app.plugin.core.memory;
import ghidra.app.cmd.memory.MoveBlockListener; import ghidra.app.cmd.memory.MoveBlockListener;
import ghidra.app.cmd.memory.MoveBlockTask; import ghidra.app.cmd.memory.MoveBlockTask;
import ghidra.framework.model.DomainObject; import ghidra.framework.model.*;
import ghidra.framework.model.DomainObjectChangedEvent; import ghidra.program.model.address.*;
import ghidra.framework.model.DomainObjectListener;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.address.AddressOverflowException;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.mem.MemoryBlock;
import ghidra.util.task.Task;
/** /**
* Model for moving a memory block; this class does validation of the new start * Model for moving a memory block; this class does validation of the new start
@ -172,11 +167,11 @@ class MoveBlockModel implements DomainObjectListener {
} }
/** /**
* Start the task that will move the block. * Create the task that will move the block
* *
* @param delay number of ms to delay until the progress dialog is displayed * @return the new task
*/ */
Task makeTask() { MoveBlockTask makeTask() {
return new MoveBlockTask(program, block.getStart(), newStartAddr, listener); return new MoveBlockTask(program, block.getStart(), newStartAddr, listener);
} }

View file

@ -57,6 +57,11 @@ public class HeadlessTimedTaskMonitor implements TaskMonitor {
// stub // stub
} }
@Override
public String getMessage() {
return null;
}
@Override @Override
public void setProgress(long value) { public void setProgress(long value) {
// stub // stub
@ -82,6 +87,11 @@ public class HeadlessTimedTaskMonitor implements TaskMonitor {
// stub // stub
} }
@Override
public boolean isIndeterminate() {
return false;
}
@Override @Override
public void checkCanceled() throws CancelledException { public void checkCanceled() throws CancelledException {
if (isCancelled()) { if (isCancelled()) {

View file

@ -30,7 +30,7 @@ import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.mem.MemoryBlock;
import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.test.TestEnv; import ghidra.test.TestEnv;
import ghidra.util.task.*; import ghidra.util.task.TaskBuilder;
public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
implements MoveBlockListener { implements MoveBlockListener {
@ -42,7 +42,7 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
private MemoryBlock block; private MemoryBlock block;
private volatile boolean moveCompleted; private volatile boolean moveCompleted;
private volatile boolean status; private volatile boolean success;
private volatile String errMsg; private volatile String errMsg;
private Program buildProgram1(String programName) throws Exception { private Program buildProgram1(String programName) throws Exception {
@ -134,7 +134,7 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
// wait until the we get the move complete notification // wait until the we get the move complete notification
waitForCondition(() -> moveCompleted && notepad.canLock()); waitForCondition(() -> moveCompleted && notepad.canLock());
assertTrue("Error message= [" + errMsg + "], ", status); assertTrue("Error message= [" + errMsg + "], ", success);
} }
@Test @Test
@ -145,7 +145,7 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
// wait until the we get the move complete notification // wait until the we get the move complete notification
waitForCondition(() -> moveCompleted && notepad.canLock()); waitForCondition(() -> moveCompleted && notepad.canLock());
assertTrue("Error message= [" + errMsg + "], ", status); assertTrue("Error message= [" + errMsg + "], ", success);
} }
@Test @Test
@ -177,7 +177,7 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
waitForCondition(() -> moveCompleted && x8051.canLock()); waitForCondition(() -> moveCompleted && x8051.canLock());
setErrorsExpected(false); setErrorsExpected(false);
assertFalse("Error message= [" + errMsg + "], ", status); assertFalse("Error message= [" + errMsg + "], ", success);
} }
@Test @Test
@ -242,15 +242,10 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
assertNotNull(errMsg); assertNotNull(errMsg);
} }
private void launch(Task task) { private void launch(MoveBlockTask task) {
BackgroundThreadTaskLauncher launcher = new BackgroundThreadTaskLauncher(task); TaskBuilder.withTask(task).launchModal();
launcher.run(new TaskMonitorAdapter() { errMsg = task.getStatusMessage();
@Override
public void setMessage(String message) {
errMsg = message;
}
});
} }
private Address getNotepadAddr(int offset) { private Address getNotepadAddr(int offset) {
@ -265,7 +260,7 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
@Override @Override
public void moveBlockCompleted(MoveBlockTask cmd) { public void moveBlockCompleted(MoveBlockTask cmd) {
moveCompleted = true; moveCompleted = true;
this.status = cmd.getStatus(); this.success = cmd.wasSuccessful();
} }
@Override @Override

View file

@ -221,8 +221,8 @@ public abstract class DataLoadingConstraintEditor<T> extends AbstractColumnConst
reloadDataButton.setVisible(false); reloadDataButton.setVisible(false);
Task task = new LoadDataTask(); Task task = new LoadDataTask();
task.addTaskListener(this); task.addTaskListener(this);
BackgroundThreadTaskLauncher launcher = new BackgroundThreadTaskLauncher(task);
launcher.run(taskMonitorComponent); TaskBuilder.withTask(task).launchInBackground(taskMonitorComponent);
} }
@Override @Override

View file

@ -30,15 +30,15 @@ import ghidra.util.TaskUtilities;
* *
* <p>See {@link TaskLauncher}. * <p>See {@link TaskLauncher}.
*/ */
public class BackgroundThreadTaskLauncher { class BackgroundThreadTaskLauncher {
private Task task; private Task task;
public BackgroundThreadTaskLauncher(Task task) { BackgroundThreadTaskLauncher(Task task) {
this.task = task; this.task = task;
} }
public void run(TaskMonitor monitor) { void run(TaskMonitor monitor) {
// add the task here, so we can track it before it is actually started by the thread // add the task here, so we can track it before it is actually started by the thread
TaskUtilities.addTrackedTask(task, monitor); TaskUtilities.addTrackedTask(task, monitor);
@ -49,5 +49,6 @@ public class BackgroundThreadTaskLauncher {
Thread.currentThread().setName(name); Thread.currentThread().setName(name);
task.monitoredRun(monitor); task.monitoredRun(monitor);
}); });
} }
} }

View file

@ -0,0 +1,158 @@
/* ###
* 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;
import ghidra.util.datastruct.WeakDataStructureFactory;
import ghidra.util.datastruct.WeakSet;
import ghidra.util.exception.CancelledException;
/**
* A task monitor that tracks all monitor state, but is not attached to any UI component
*
* <p><b>Synchronization Policy</b>:<br>
* We wish for this class to be performant. Thus, we do not synchronize the methods of this
* class, nor do we make the values thread visible via <code>volatile</code> or by any of
* the Java concurrent structures (e.g., {@link AtomicBoolean}). In order to keep the values of
* this class's fields update-to-date, we have chosen to synchronize the package-level client of
* this class. <b>If this class is ever made public, then most of the methods herein need to
* be synchronized to prevent race conditions and to provide visibility.
*/
class BasicTaskMonitor implements TaskMonitor {
private WeakSet<CancelledListener> listeners =
WeakDataStructureFactory.createCopyOnReadWeakSet();
private String message;
private long progress;
private long maxProgress;
private boolean cancelEnabled = true;
private boolean isCancelled;
private boolean isIndeterminate;
@Override
public void addCancelledListener(CancelledListener l) {
listeners.add(l);
}
@Override
public void removeCancelledListener(CancelledListener l) {
listeners.remove(l);
}
@Override
public void incrementProgress(long incrementAmount) {
setProgress(progress + incrementAmount);
}
@Override
public long getProgress() {
return progress;
}
@Override
public boolean isCancelled() {
return isCancelled;
}
@Override
public void checkCanceled() throws CancelledException {
if (isCancelled) {
throw new CancelledException();
}
}
@Override
public void setMessage(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
@Override
public void setProgress(long value) {
progress = value;
}
@Override
public void initialize(long maxValue) {
setMaximum(maxValue);
setProgress(0);
}
@Override
public void setMaximum(long max) {
this.maxProgress = max;
if (progress > max) {
progress = max;
}
}
@Override
public void setIndeterminate(boolean indeterminate) {
isIndeterminate = indeterminate;
}
@Override
public void setCancelEnabled(boolean enable) {
cancelEnabled = enable;
}
@Override
public boolean isCancelEnabled() {
return cancelEnabled;
}
@Override
public void cancel() {
boolean wasCancelled = isCancelled;
isCancelled = true;
if (!wasCancelled) {
notifyCancelledListeners();
}
}
@Override
public void clearCanceled() {
isCancelled = false;
}
@Override
public long getMaximum() {
return maxProgress;
}
@Override
public boolean isIndeterminate() {
return isIndeterminate;
}
@Override
public void setShowProgressValue(boolean showProgressValue) {
// stub
}
private void notifyCancelledListeners() {
for (CancelledListener listener : listeners) {
listener.cancelled();
}
}
}

View file

@ -255,6 +255,22 @@ public class TaskBuilder {
new TaskLauncher(t, parent, delay, dialogWidth); new TaskLauncher(t, parent, delay, dialogWidth);
} }
/**
* Runs the task in a background thread with the given monitor that cannot be null. This
* is a special case for clients that already have a task monitor widget in their UI and
* they wish to let it show the progress of the given task while not blocking the Swing
* thread.
*
* @param monitor the task monitor; may not be null
*/
public void launchInBackground(TaskMonitor monitor) {
// validate(); // not needed since we are in the background
Objects.requireNonNull(monitor);
BackgroundThreadTaskLauncher launcher =
new BackgroundThreadTaskLauncher(new TaskBuilderTask(false));
launcher.run(monitor);
}
private void validate() { private void validate() {
if (title == null) { if (title == null) {
throw new NullPointerException("Task title cannot be null"); throw new NullPointerException("Task title cannot be null");

View file

@ -18,6 +18,7 @@ package ghidra.util.task;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Component; import java.awt.Component;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import javax.swing.*; import javax.swing.*;
@ -50,14 +51,13 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
private Runnable shouldCancelRunnable; private Runnable shouldCancelRunnable;
private boolean taskDone; private boolean taskDone;
private JPanel mainPanel; private JPanel mainPanel;
private ChompingBitsAnimationPanel chompingBitsPanel; private JPanel activityPanel;
private TaskMonitorComponent monitorComponent; private TaskMonitorComponent monitorComponent;
/** Runnable that updates the primary message label in the dialog */
private Runnable updatePrimaryMessageRunnable;
/** If not null, then the value of the string has yet to be rendered */ /** If not null, then the value of the string has yet to be rendered */
private String newPrimaryMessage; private AtomicReference<String> newMessage = new AtomicReference<>();
private SwingUpdateManager messageUpdater =
new SwingUpdateManager(100, 250, () -> setStatusText(newMessage.getAndSet(null)));
/** /**
* Constructor * Constructor
@ -112,7 +112,7 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
private void setup(boolean canCancel, boolean hasProgress) { private void setup(boolean canCancel, boolean hasProgress) {
monitorComponent = new TaskMonitorComponent(false, false); monitorComponent = new TaskMonitorComponent(false, false);
chompingBitsPanel = new ChompingBitsAnimationPanel(); activityPanel = new ChompingBitsAnimationPanel();
setCancelEnabled(canCancel); setCancelEnabled(canCancel);
setRememberLocation(false); setRememberLocation(false);
@ -122,12 +122,7 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
close(); close();
dispose(); dispose();
}; };
updatePrimaryMessageRunnable = () -> {
setStatusText(newPrimaryMessage);
synchronized (TaskDialog.this) {
newPrimaryMessage = null;
}
};
shouldCancelRunnable = () -> { shouldCancelRunnable = () -> {
int currentTaskID = taskID.get(); int currentTaskID = taskID.get();
@ -160,10 +155,9 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
* *
* @return true if the task should be cancelled * @return true if the task should be cancelled
*/ */
protected boolean promptToVerifyCancel() { private boolean promptToVerifyCancel() {
boolean userSaysYes = OptionDialog.showYesNoDialog(getComponent(), "Cancel?", boolean userSaysYes = OptionDialog.showYesNoDialog(getComponent(), "Cancel?",
"Do you really want to cancel \"" + getTitle() + "\"?") == OptionDialog.OPTION_ONE; "Do you really want to cancel \"" + getTitle() + "\"?") == OptionDialog.OPTION_ONE;
return userSaysYes; return userSaysYes;
} }
@ -179,13 +173,13 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
} }
/** /**
* Adds the panel that contains the chomping bits animation to the dialog. This should only be * Adds the panel that contains the activity panel (e.g., the eating bits animation) to the
* called if the dialog has no need to display progress. * dialog. This should only be called if the dialog has no need to display progress.
*/ */
private void installActivityDisplay() { private void installActivityDisplay() {
SystemUtilities.runIfSwingOrPostSwingLater(() -> { SystemUtilities.runIfSwingOrPostSwingLater(() -> {
mainPanel.removeAll(); mainPanel.removeAll();
mainPanel.add(chompingBitsPanel, BorderLayout.CENTER); mainPanel.add(activityPanel, BorderLayout.CENTER);
repack(); repack();
}); });
} }
@ -203,77 +197,11 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
close(); close();
} }
@Override
public void setShowProgressValue(boolean showProgressValue) {
monitorComponent.setShowProgressValue(showProgressValue);
}
/**
* Sets the percentage done
*
* @param param The percentage of the task completed.
*/
@Override
public void setProgress(long param) {
monitorComponent.setProgress(param);
}
@Override
public void initialize(long max) {
if (monitorComponent.isIndeterminate()) {
// don't show the progress bar if we have already been marked as indeterminate (this
// allows us to prevent low-level algorithms from changing the display settings).
return;
}
if (!monitorComponent.isShowing()) {
installProgressMonitor();
}
monitorComponent.initialize(max);
}
@Override
public void setMaximum(long max) {
monitorComponent.setMaximum(max);
}
@Override
public long getMaximum() {
return monitorComponent.getMaximum();
}
/**
* Sets the <code>indeterminate</code> property of the progress bar,
* which determines whether the progress bar is in determinate
* or indeterminate mode.
* An indeterminate progress bar continuously displays animation
* indicating that an operation of unknown length is occurring.
* By default, this property is <code>false</code>.
* Some look and feels might not support indeterminate progress bars;
* they will ignore this property.
*
* @see JProgressBar
*/
@Override
public void setIndeterminate(final boolean indeterminate) {
monitorComponent.setIndeterminate(indeterminate);
}
@Override @Override
protected void cancelCallback() { protected void cancelCallback() {
SwingUtilities.invokeLater(shouldCancelRunnable); SwingUtilities.invokeLater(shouldCancelRunnable);
} }
@Override
synchronized public void setMessage(String str) {
boolean invoke = (newPrimaryMessage == null);
if (invoke) {
newPrimaryMessage = str;
SwingUtilities.invokeLater(updatePrimaryMessageRunnable);
}
}
@Override @Override
public void setCancelEnabled(boolean enable) { public void setCancelEnabled(boolean enable) {
monitorComponent.setCancelEnabled(enable); monitorComponent.setCancelEnabled(enable);
@ -300,11 +228,6 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
return taskDone; return taskDone;
} }
@Override
public boolean isCancelled() {
return monitorComponent.isCancelled();
}
/** /**
* Shows the dialog window centered on the parent window. * Shows the dialog window centered on the parent window.
* Dialog display is delayed if delay greater than zero. * Dialog display is delayed if delay greater than zero.
@ -382,6 +305,71 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
SystemUtilities.runSwingNow(disposeTask); SystemUtilities.runSwingNow(disposeTask);
} }
//==================================================================================================
// TaskMonitor Methods
//==================================================================================================
@Override
public void setMessage(String str) {
newMessage.set(str);
messageUpdater.update();
}
@Override
public String getMessage() {
return monitorComponent.getMessage();
}
@Override
public void setShowProgressValue(boolean showProgressValue) {
monitorComponent.setShowProgressValue(showProgressValue);
}
@Override
public void setProgress(long progress) {
monitorComponent.setProgress(progress);
}
@Override
public void initialize(long max) {
if (monitorComponent.isIndeterminate()) {
// don't show the progress bar if we have already been marked as indeterminate (this
// allows us to prevent low-level algorithms from changing the display settings).
return;
}
if (!monitorComponent.isShowing()) {
installProgressMonitor();
}
monitorComponent.initialize(max);
}
@Override
public void setMaximum(long max) {
monitorComponent.setMaximum(max);
}
@Override
public long getMaximum() {
return monitorComponent.getMaximum();
}
@Override
public void setIndeterminate(final boolean indeterminate) {
monitorComponent.setIndeterminate(indeterminate);
}
@Override
public boolean isIndeterminate() {
return monitorComponent.isIndeterminate();
}
@Override
public boolean isCancelled() {
return monitorComponent.isCancelled();
}
@Override @Override
public synchronized void cancel() { public synchronized void cancel() {
if (monitorComponent.isCancelled()) { if (monitorComponent.isCancelled()) {
@ -421,4 +409,8 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
public void removeCancelledListener(CancelledListener listener) { public void removeCancelledListener(CancelledListener listener) {
monitorComponent.removeCancelledListener(listener); monitorComponent.removeCancelledListener(listener);
} }
//==================================================================================================
// End TaskMonitor Methods
//==================================================================================================
} }

View file

@ -16,10 +16,8 @@
package ghidra.util.task; package ghidra.util.task;
import java.awt.Component; import java.awt.Component;
import java.util.concurrent.TimeUnit;
import ghidra.util.Swing; import ghidra.util.Swing;
import ghidra.util.exception.UnableToSwingException;
/** /**
* Class to initiate a Task in a new Thread, and to show a progress dialog that indicates * Class to initiate a Task in a new Thread, and to show a progress dialog that indicates
@ -30,14 +28,6 @@ import ghidra.util.exception.UnableToSwingException;
* {@link #TaskLauncher(Task, Component, int, int)}. Alternatively, for simpler uses, * {@link #TaskLauncher(Task, Component, int, int)}. Alternatively, for simpler uses,
* see one of the many static convenience methods. * see one of the many static convenience methods.
* *
* <p><b>Important Usage Note:</b><br>
* For clients that are not on the Swing thread the behavior of this class is designed to
* prevent deadlocks. When called from a non-Swing thread, this class will attempt to show a
* modal dialog. However, if more than {@link #getSwingTimeoutInSeconds()} elapses while waiting
* for the Swing thread, then this class will <b>give up on using the Swing thread and will not
* create a background thread</b>. Instead, the client code will be run in the client thread.
*
* <a name="modal_usage"></a>
* <p><b><a name="modal_usage">Modal Usage</a></b><br> * <p><b><a name="modal_usage">Modal Usage</a></b><br>
* Most clients of this class should not be concerned with where * Most clients of this class should not be concerned with where
* the dialog used by this class will appear. By default, it will be shown over * the dialog used by this class will appear. By default, it will be shown over
@ -229,37 +219,8 @@ public class TaskLauncher {
*/ */
public TaskLauncher(Task task, Component parent, int delayMs, int dialogWidth) { public TaskLauncher(Task task, Component parent, int delayMs, int dialogWidth) {
try {
scheduleFromSwingThread(task, parent, delayMs, dialogWidth);
}
catch (UnableToSwingException e) {
runInThisBackgroundThread(task);
}
}
private void scheduleFromSwingThread(Task task, Component parent, int delayMs, int dialogWidth)
throws UnableToSwingException {
TaskRunner runner = createTaskRunner(task, parent, delayMs, dialogWidth); TaskRunner runner = createTaskRunner(task, parent, delayMs, dialogWidth);
if (Swing.isEventDispatchThread()) {
runner.run(); runner.run();
return;
}
//
// Not on the Swing thread. Try to execute on the Swing thread, timing-out if it takes
// too long (this prevents deadlocks).
//
// This will throw an exception if we could not get the Swing lock. When that happens,
// the task was NOT run.
int timeout = getSwingTimeoutInSeconds();
Swing.runNow(() -> runner.run(), timeout, TimeUnit.SECONDS);
}
// template method to allow timeout change; used by tests
protected int getSwingTimeoutInSeconds() {
return 2;
} }
// template method to allow task runner change; used by tests // template method to allow task runner change; used by tests
@ -275,7 +236,7 @@ public class TaskLauncher {
* @throws IllegalStateException if the given thread is the Swing thread * @throws IllegalStateException if the given thread is the Swing thread
*/ */
protected void runInThisBackgroundThread(Task task) { protected void runInThisBackgroundThread(Task task) {
if (Swing.isEventDispatchThread()) { if (Swing.isSwingThread()) {
throw new IllegalStateException("Must not call this method from the Swing thread"); throw new IllegalStateException("Must not call this method from the Swing thread");
} }

View file

@ -155,6 +155,11 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
startUpdateTimer(); startUpdateTimer();
} }
@Override
public synchronized String getMessage() {
return progressMessage;
}
@Override @Override
public synchronized void setProgress(long value) { public synchronized void setProgress(long value) {
if (progress == value) { if (progress == value) {

View file

@ -16,12 +16,12 @@
package ghidra.util.task; package ghidra.util.task;
import java.awt.Component; import java.awt.Component;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import generic.concurrent.GThreadPool; import generic.concurrent.GThreadPool;
import generic.util.WindowUtilities; import generic.util.WindowUtilities;
import ghidra.util.Swing; import ghidra.util.*;
import ghidra.util.TaskUtilities;
/** /**
* Helper class to launch the given task in a background thread, showing a task dialog if * Helper class to launch the given task in a background thread, showing a task dialog if
@ -29,20 +29,13 @@ import ghidra.util.TaskUtilities;
*/ */
class TaskRunner { class TaskRunner {
protected Task task; private Task task;
private Component parent; private Component parent;
private int delayMs; private int delayMs;
private int dialogWidth; private int dialogWidth;
private TaskDialog taskDialog; private TaskDialog taskDialog;
private Thread taskThread; private CountDownLatch finished = new CountDownLatch(1);
private CancelledListener monitorChangeListener = () -> {
if (task.isInterruptible()) {
taskThread.interrupt();
}
if (task.isForgettable()) {
taskDialog.close(); // close the dialog and forget about the task
}
};
TaskRunner(Task task, Component parent, int delayMs, int dialogWidth) { TaskRunner(Task task, Component parent, int delayMs, int dialogWidth) {
this.task = task; this.task = task;
@ -53,37 +46,40 @@ class TaskRunner {
void run() { void run() {
// note: we need to be on the Swing thread to create our UI widgets BasicTaskMonitor internalMonitor = new BasicTaskMonitor();
Swing.assertThisIsTheSwingThread( WrappingTaskMonitor monitor = new WrappingTaskMonitor(internalMonitor);
"The Task runner is required to be run from the Swing thread"); startTaskThread(monitor);
this.taskDialog = buildTaskDialog(parent); Swing.runIfSwingOrRunLater(() -> showTaskDialog(monitor));
waitForModalTask();
startBackgroundThread(taskDialog);
taskDialog.show(Math.max(delayMs, 0));
} }
protected TaskDialog buildTaskDialog(Component comp) { private void waitForModalTask() {
// if (!task.isModal()) {
// This class may be used by background threads. Make sure that our GUI creation is return; // we do not wait for non-modal tasks
// on the Swing thread to prevent exceptions while painting (as seen when using the
// Nimbus Look and Feel).
//
taskDialog = createTaskDialog(comp);
taskDialog.setMinimumSize(dialogWidth, 0);
if (task.isInterruptible() || task.isForgettable()) {
taskDialog.addCancelledListener(monitorChangeListener);
} }
taskDialog.setStatusJustification(task.getStatusTextAlignment()); try {
// fun note: if this is the Swing thread, then it will not wait, as the Swing thread
return taskDialog; // was blocked by the modal dialog in the call before this one
finished.await();
}
catch (InterruptedException e) {
Msg.debug(this, "Task Launcher unexpectedly interrupted waiting for task thread", e);
}
} }
private void startBackgroundThread(TaskMonitor monitor) { // protected to allow for dependency injection
protected TaskDialog buildTaskDialog(Component comp, TaskMonitor monitor) {
TaskDialog dialog = createTaskDialog(comp);
dialog.setMinimumSize(dialogWidth, 0);
dialog.setStatusJustification(task.getStatusTextAlignment());
return dialog;
}
private void startTaskThread(TaskMonitor monitor) {
// add the task here, so we can track it before it is actually started by the thread // add the task here, so we can track it before it is actually started by the thread
TaskUtilities.addTrackedTask(task, monitor); TaskUtilities.addTrackedTask(task, monitor);
@ -93,20 +89,61 @@ class TaskRunner {
Executor executor = pool.getExecutor(); Executor executor = pool.getExecutor();
executor.execute(() -> { executor.execute(() -> {
Thread.currentThread().setName(name); Thread.currentThread().setName(name);
try {
task.monitoredRun(monitor); task.monitoredRun(monitor);
taskDialog.taskProcessed(); }
finally {
taskFinished();
}
}); });
} }
private TaskDialog createTaskDialog(Component comp) { private TaskDialog createTaskDialog(Component comp) {
Component currentParent = comp; Component centerOverComponent = comp;
Component currentParent = centerOverComponent;
if (currentParent != null) { if (currentParent != null) {
currentParent = WindowUtilities.windowForComponent(comp); currentParent = WindowUtilities.windowForComponent(comp);
} }
if (currentParent == null) { if (currentParent == null) {
return new TaskDialog(task); centerOverComponent = null;
} }
return new TaskDialog(comp, task);
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.assertThisIsTheSwingThread("Must be on the Swing thread build the Task Dialog");
taskDialog = buildTaskDialog(parent, monitor);
monitor.setDelegate(taskDialog); // initialize the dialog to the current state of the monitor
taskDialog.show(Math.max(delayMs, 0));
}
/*testing*/ boolean isFinished() {
return finished.getCount() == 0;
}
private void taskFinished() {
finished.countDown();
// Do this later on the Swing thread to handle the race condition where the dialog
// did not exist at the time of this call, but was in the process of being created
Swing.runLater(() -> {
if (taskDialog != null) {
taskDialog.taskProcessed();
}
});
} }
} }

View file

@ -64,6 +64,14 @@ public class AbstractTaskTest extends AbstractDockingTest {
} }
} }
protected void assertNoDialogShown() {
if (dialogSpy == null) {
return; // not shown
}
assertFalse(dialogSpy.wasShown());
}
protected void waitForTask() throws Exception { protected void waitForTask() throws Exception {
threadsFinished.await(2, TimeUnit.SECONDS); threadsFinished.await(2, TimeUnit.SECONDS);
} }
@ -97,18 +105,18 @@ public class AbstractTaskTest extends AbstractDockingTest {
return new TaskRunner(task, parent, delay, dialogWidth) { return new TaskRunner(task, parent, delay, dialogWidth) {
@Override @Override
protected TaskDialog buildTaskDialog(Component comp) { protected TaskDialog buildTaskDialog(Component comp, TaskMonitor monitor) {
dialogSpy = new TaskDialogSpy(task); dialogSpy = new TaskDialogSpy(task) {
@Override
public synchronized boolean isCompleted() {
return super.isCompleted() || isFinished();
}
};
return dialogSpy; return dialogSpy;
} }
}; };
} }
@Override
protected int getSwingTimeoutInSeconds() {
return 1; // speed-up for tests
}
@Override @Override
protected void runInThisBackgroundThread(Task task) { protected void runInThisBackgroundThread(Task task) {
didRunInBackground.set(true); didRunInBackground.set(true);

View file

@ -27,6 +27,21 @@ public class TaskDialogTest extends AbstractTaskTest {
waitForSwing(); waitForSwing();
} }
@Test
public void testModalDialogWithoutDependencyInjection() throws Exception {
//
// A version of the test to use all of the real dialog internals of the
// TaskRunner, which are usually replaced with test versions.
//
FastModalTask task = new FastModalTask();
new TaskLauncher(task);
waitForTasks(); // make sure we don't timeout
}
@Test @Test
public void testModalDialog_FastTask_NoDialog() throws Exception { public void testModalDialog_FastTask_NoDialog() throws Exception {
@ -62,7 +77,7 @@ public class TaskDialogTest extends AbstractTaskTest {
waitForTask(); waitForTask();
assertFalse(dialogSpy.wasShown()); assertFalse(dialogSpy.wasShown());
assertSwingThreadFinishedBeforeTask(); assertNoDialogShown();
} }
@Test @Test
@ -111,5 +126,4 @@ public class TaskDialogTest extends AbstractTaskTest {
assertFalse(dialogSpy.isCancelEnabled()); assertFalse(dialogSpy.isCancelEnabled());
} }
} }

View file

@ -40,15 +40,20 @@ import ghidra.util.task.TaskMonitor;
* On ConcurrentQs that only allow one task to run at a time, when a task is cancelled, * On ConcurrentQs that only allow one task to run at a time, when a task is cancelled,
* the next task can begin. Most likely, the thread that was running the cancelled * the next task can begin. Most likely, the thread that was running the cancelled
* task won't be free, and a new thread will be used to start running the next task. * task won't be free, and a new thread will be used to start running the next task.
*
* @param <I> the input type
* @param <R> the output type
*/ */
class FutureTaskMonitor<I, R> extends FutureTask<R> implements TaskMonitor { class FutureTaskMonitor<I, R> extends FutureTask<R> implements TaskMonitor {
private final ConcurrentQ<I, R> queue; private final ConcurrentQ<I, R> queue;
private final I item; private final I item;
private final long id; private final long id;
private volatile String lastMessage;
private volatile long currentProgress; private volatile long currentProgress;
private volatile long maxProgress; private volatile long maxProgress;
private volatile CancelledListener cancelledListener; private volatile CancelledListener cancelledListener;
private volatile boolean isIndeterminate;
FutureTaskMonitor(ConcurrentQ<I, R> queue, Callable<R> callable, I item, long id) { FutureTaskMonitor(ConcurrentQ<I, R> queue, Callable<R> callable, I item, long id) {
super(callable); super(callable);
@ -103,6 +108,11 @@ class FutureTaskMonitor<I, R> extends FutureTask<R> implements TaskMonitor {
queue.progressMessageChanged(id, item, message); queue.progressMessageChanged(id, item, message);
} }
@Override
public String getMessage() {
return lastMessage;
}
@Override @Override
public void initialize(long max) { public void initialize(long max) {
currentProgress = 0; currentProgress = 0;
@ -123,9 +133,15 @@ class FutureTaskMonitor<I, R> extends FutureTask<R> implements TaskMonitor {
@Override @Override
public void setIndeterminate(boolean indeterminate) { public void setIndeterminate(boolean indeterminate) {
this.isIndeterminate = indeterminate;
queue.progressModeChanged(id, item, indeterminate); queue.progressModeChanged(id, item, indeterminate);
} }
@Override
public boolean isIndeterminate() {
return isIndeterminate;
}
@Override @Override
public long getProgress() { public long getProgress() {
return currentProgress; return currentProgress;

View file

@ -220,28 +220,6 @@ public abstract class Task implements MonitoredRunnable {
return isModal; return isModal;
} }
public boolean isInterruptible() {
return isInterruptible;
}
public void setInterruptible(boolean interruptible) {
this.isInterruptible = interruptible;
}
/**
* Returns true if this task should be left alone to die when cancelled, as opposed to being
* interrupted
*
* @return true if forgettable
*/
public boolean isForgettable() {
return isForgettable;
}
public void setForgettable(boolean isForgettable) {
this.isForgettable = isForgettable;
}
/** /**
* Sets the task listener on this task. It is a programming error to call this method more * Sets the task listener on this task. It is a programming error to call this method more
* than once or to call this method if a listener was passed into the constructor of this class. * than once or to call this method if a listener was passed into the constructor of this class.

View file

@ -67,6 +67,11 @@ public class TaskMonitorAdapter implements TaskMonitor {
// do nothing // do nothing
} }
@Override
public String getMessage() {
return null;
}
@Override @Override
public void setProgress(long value) { public void setProgress(long value) {
// do nothing // do nothing
@ -105,6 +110,11 @@ public class TaskMonitorAdapter implements TaskMonitor {
// do nothing // do nothing
} }
@Override
public boolean isIndeterminate() {
return false;
}
@Override @Override
public synchronized void setCancelEnabled(boolean enable) { public synchronized void setCancelEnabled(boolean enable) {
cancelEnabled = enable; cancelEnabled = enable;
@ -134,6 +144,8 @@ public class TaskMonitorAdapter implements TaskMonitor {
} }
cancelled = false; cancelled = false;
} }
// TODO this seems like a mistake, to notify of 'cancelled' when clearning
notifyChangeListeners(); notifyChangeListeners();
} }

View file

@ -136,6 +136,11 @@ public class TaskMonitorSplitter {
parent.setIndeterminate(indeterminate); parent.setIndeterminate(indeterminate);
} }
@Override
public boolean isIndeterminate() {
return parent.isIndeterminate();
}
@Override @Override
public void initialize(long newMax) { public void initialize(long newMax) {
setMaximum(newMax); setMaximum(newMax);
@ -152,7 +157,11 @@ public class TaskMonitorSplitter {
@Override @Override
public void setMessage(String message) { public void setMessage(String message) {
parent.setMessage(message); parent.setMessage(message);
}
@Override
public String getMessage() {
return parent.getMessage();
} }
@Override @Override

View file

@ -139,6 +139,11 @@ public class TimeoutTaskMonitor implements TaskMonitor {
delegate.setMessage(message); delegate.setMessage(message);
} }
@Override
public String getMessage() {
return delegate.getMessage();
}
@Override @Override
public void setProgress(long value) { public void setProgress(long value) {
delegate.setProgress(value); delegate.setProgress(value);
@ -164,6 +169,11 @@ public class TimeoutTaskMonitor implements TaskMonitor {
delegate.setIndeterminate(indeterminate); delegate.setIndeterminate(indeterminate);
} }
@Override
public boolean isIndeterminate() {
return delegate.isIndeterminate();
}
@Override @Override
public void checkCanceled() throws CancelledException { public void checkCanceled() throws CancelledException {
if (didTimeout()) { if (didTimeout()) {

View file

@ -15,16 +15,30 @@
*/ */
package ghidra.util.task; package ghidra.util.task;
import ghidra.util.datastruct.WeakDataStructureFactory;
import ghidra.util.datastruct.WeakSet;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
/** /**
* An implementation of the {@link TaskMonitor} interface that simply wraps a delegate task * An implementation of the {@link TaskMonitor} interface that simply wraps a delegate task
* monitor. This is useful for classes that wish to wrap a task monitor, changing behavior * monitor. This is useful for classes that wish to wrap a task monitor, changing behavior
* as needed by overriding a subset of methods. * as needed by overriding a subset of methods.
*
* <p><b>Synchronization Policy</b>:<br>
* We wish for this class to be performant. Thus, we do not synchronize the methods of this
* class. The {@link #setDelegate(TaskMonitor)} is synchronized to ensure thread visibility
* for the state of the delegate monitor.
*
* <p>When calling {@link #setDelegate(TaskMonitor)} there is the potential for the values being
* transferred to become inconsistent with any new values being set. We have decided that this
* does not much matter for the overall progress or the messages on the monitor. However, most
* of the other setter methods could lead to bad behavior if they are inconsistent.
*/ */
public class WrappingTaskMonitor implements TaskMonitor { public class WrappingTaskMonitor implements TaskMonitor {
protected final TaskMonitor delegate; private WeakSet<CancelledListener> listeners =
WeakDataStructureFactory.createCopyOnReadWeakSet();
protected TaskMonitor delegate;
/** /**
* Constructor * Constructor
@ -35,8 +49,36 @@ public class WrappingTaskMonitor implements TaskMonitor {
this.delegate = delegate; this.delegate = delegate;
} }
/**
* Sets the delegate of this wrapper to be the new value. The new delegate will be
* initialized with the current values of the existing delegate.
*
* @param newDelegate the new delegate
*/
public synchronized void setDelegate(TaskMonitor newDelegate) {
// if the existing monitor has already been cancelled, then do not apply the state
if (delegate.isCancelled()) {
newDelegate.cancel();
return;
}
for (CancelledListener l : listeners) {
newDelegate.addCancelledListener(l);
delegate.removeCancelledListener(l);
}
newDelegate.setMaximum(delegate.getMaximum());
newDelegate.setProgress(delegate.getProgress());
newDelegate.setMessage(delegate.getMessage());
newDelegate.setIndeterminate(delegate.isIndeterminate());
newDelegate.setCancelEnabled(delegate.isCancelEnabled());
this.delegate = newDelegate;
}
@Override @Override
public boolean isCancelled() { public synchronized boolean isCancelled() {
return delegate.isCancelled(); return delegate.isCancelled();
} }
@ -50,6 +92,11 @@ public class WrappingTaskMonitor implements TaskMonitor {
delegate.setMessage(message); delegate.setMessage(message);
} }
@Override
public String getMessage() {
return delegate.getMessage();
}
@Override @Override
public void setProgress(long value) { public void setProgress(long value) {
delegate.setProgress(value); delegate.setProgress(value);
@ -61,7 +108,7 @@ public class WrappingTaskMonitor implements TaskMonitor {
} }
@Override @Override
public void setMaximum(long max) { public synchronized void setMaximum(long max) {
delegate.setMaximum(max); delegate.setMaximum(max);
} }
@ -71,10 +118,15 @@ public class WrappingTaskMonitor implements TaskMonitor {
} }
@Override @Override
public void setIndeterminate(boolean indeterminate) { public synchronized void setIndeterminate(boolean indeterminate) {
delegate.setIndeterminate(indeterminate); delegate.setIndeterminate(indeterminate);
} }
@Override
public boolean isIndeterminate() {
return delegate.isIndeterminate();
}
@Override @Override
public void checkCanceled() throws CancelledException { public void checkCanceled() throws CancelledException {
delegate.checkCanceled(); delegate.checkCanceled();
@ -91,22 +143,24 @@ public class WrappingTaskMonitor implements TaskMonitor {
} }
@Override @Override
public void cancel() { public synchronized void cancel() {
delegate.cancel(); delegate.cancel();
} }
@Override @Override
public void addCancelledListener(CancelledListener listener) { public synchronized void addCancelledListener(CancelledListener listener) {
listeners.add(listener);
delegate.addCancelledListener(listener); delegate.addCancelledListener(listener);
} }
@Override @Override
public void removeCancelledListener(CancelledListener listener) { public synchronized void removeCancelledListener(CancelledListener listener) {
listeners.remove(listener);
delegate.removeCancelledListener(listener); delegate.removeCancelledListener(listener);
} }
@Override @Override
public void setCancelEnabled(boolean enable) { public synchronized void setCancelEnabled(boolean enable) {
delegate.setCancelEnabled(enable); delegate.setCancelEnabled(enable);
} }
@ -116,7 +170,7 @@ public class WrappingTaskMonitor implements TaskMonitor {
} }
@Override @Override
public void clearCanceled() { public synchronized void clearCanceled() {
delegate.clearCanceled(); delegate.clearCanceled();
} }
} }

View file

@ -39,7 +39,7 @@ class LockingTaskMonitor implements TaskMonitor {
* be done in a try/finally block to avoid accidentally locking the * be done in a try/finally block to avoid accidentally locking the
* domain object indefinitely. * domain object indefinitely.
* @param dobj domain object * @param dobj domain object
* @param hasProgress * @param hasProgress true if this monitorhas progress
* @param title task title * @param title task title
*/ */
LockingTaskMonitor(DomainObjectAdapterDB dobj, boolean hasProgress, String title) { LockingTaskMonitor(DomainObjectAdapterDB dobj, boolean hasProgress, String title) {
@ -119,6 +119,11 @@ class LockingTaskMonitor implements TaskMonitor {
} }
} }
@Override
public synchronized String getMessage() {
return msg;
}
/* /*
* @see ghidra.util.task.TaskMonitor#setProgress(int) * @see ghidra.util.task.TaskMonitor#setProgress(int)
*/ */
@ -165,6 +170,11 @@ class LockingTaskMonitor implements TaskMonitor {
} }
} }
@Override
public boolean isIndeterminate() {
return indeterminate;
}
/* /*
* @see ghidra.util.task.TaskMonitor#setCancelEnabled(boolean) * @see ghidra.util.task.TaskMonitor#setCancelEnabled(boolean)
*/ */
@ -206,9 +216,6 @@ class LockingTaskMonitor implements TaskMonitor {
} }
} }
/*
* @see ghidra.util.task.TaskMonitor#checkCanceled()
*/
@Override @Override
public void checkCanceled() throws CancelledException { public void checkCanceled() throws CancelledException {
if (isCancelled()) { if (isCancelled()) {
@ -216,17 +223,11 @@ class LockingTaskMonitor implements TaskMonitor {
} }
} }
/**
* @see ghidra.util.task.TaskMonitor#incrementProgress(int)
*/
@Override @Override
public void incrementProgress(long incrementAmount) { public void incrementProgress(long incrementAmount) {
setProgress(curProgress + incrementAmount); setProgress(curProgress + incrementAmount);
} }
/**
* @see ghidra.util.task.TaskMonitor#getProgress()
*/
@Override @Override
public long getProgress() { public long getProgress() {
return curProgress; return curProgress;

View file

@ -102,6 +102,11 @@ public class GTaskMonitor implements TaskMonitor, CancelledListener {
} }
} }
@Override
public boolean isIndeterminate() {
return indeterminate;
}
@Override @Override
public void checkCanceled() throws CancelledException { public void checkCanceled() throws CancelledException {
if (isCancelled) { if (isCancelled) {
@ -163,6 +168,7 @@ public class GTaskMonitor implements TaskMonitor, CancelledListener {
return showProgressValue; return showProgressValue;
} }
@Override
public String getMessage() { public String getMessage() {
return message; return message;
} }

View file

@ -33,7 +33,7 @@ public class Swing {
private static final String SWING_TIMEOUT_SECONDS_PROPERTY = private static final String SWING_TIMEOUT_SECONDS_PROPERTY =
Swing.class.getName().toLowerCase() + ".timeout.seconds"; Swing.class.getName().toLowerCase() + ".timeout.seconds";
private static final int SWING_TIMEOUT_SECONDS_DEFAULT_VALUE = 10; private static final int SWING_TIMEOUT_SECONDS_DEFAULT_VALUE = 20;
private static int loadTimeout() { private static int loadTimeout() {
String timeoutString = System.getProperty(SWING_TIMEOUT_SECONDS_PROPERTY, String timeoutString = System.getProperty(SWING_TIMEOUT_SECONDS_PROPERTY,
@ -61,7 +61,7 @@ public class Swing {
* *
* @return true if this is the event dispatch thread -OR- is in headless mode. * @return true if this is the event dispatch thread -OR- is in headless mode.
*/ */
public static boolean isEventDispatchThread() { public static boolean isSwingThread() {
if (isInHeadlessMode()) { if (isInHeadlessMode()) {
return true; return true;
} }
@ -94,7 +94,7 @@ public class Swing {
return; // squash during production mode return; // squash during production mode
} }
if (!isEventDispatchThread()) { if (!isSwingThread()) {
Throwable t = Throwable t =
ReflectionUtilities.filterJavaThrowable(new AssertException(errorMessage)); ReflectionUtilities.filterJavaThrowable(new AssertException(errorMessage));
Msg.error(SystemUtilities.class, errorMessage, t); Msg.error(SystemUtilities.class, errorMessage, t);

View file

@ -422,7 +422,7 @@ public class SystemUtilities {
* @return true if this is the event dispatch thread -OR- is in headless mode. * @return true if this is the event dispatch thread -OR- is in headless mode.
*/ */
public static boolean isEventDispatchThread() { public static boolean isEventDispatchThread() {
return Swing.isEventDispatchThread(); return Swing.isSwingThread();
} }
/** /**

View file

@ -33,7 +33,11 @@ class StubTaskMonitor implements TaskMonitor {
@Override @Override
public void setMessage(String message) { public void setMessage(String message) {
// stub // stub
}
@Override
public String getMessage() {
return null;
} }
@Override @Override
@ -64,6 +68,11 @@ class StubTaskMonitor implements TaskMonitor {
// stub // stub
} }
@Override
public boolean isIndeterminate() {
return false;
}
@Override @Override
public void checkCanceled() throws CancelledException { public void checkCanceled() throws CancelledException {
// stub // stub

View file

@ -56,6 +56,12 @@ public interface TaskMonitor {
*/ */
public void setMessage(String message); public void setMessage(String message);
/**
* Gets the last set message of this monitor
* @return the message
*/
public String getMessage();
/** /**
* Sets the current progress value * Sets the current progress value
* @param value progress value * @param value progress value
@ -91,6 +97,12 @@ public interface TaskMonitor {
*/ */
public void setIndeterminate(boolean indeterminate); public void setIndeterminate(boolean indeterminate);
/**
* Returns true if this monitor shows no progress
* @return true if this monitor shows no progress
*/
public boolean isIndeterminate();
/** /**
* Check to see if this monitor has been canceled * Check to see if this monitor has been canceled
* @throws CancelledException if monitor has been cancelled * @throws CancelledException if monitor has been cancelled