mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
Task Launcher - changed launcher to always start the background thread
before creating the dialog
This commit is contained in:
parent
454ce9c817
commit
27f21edba7
19 changed files with 470 additions and 165 deletions
|
@ -1467,12 +1467,22 @@ public class AutoAnalysisManager implements DomainObjectListener, DomainObjectCl
|
|||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIndeterminate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessage(String message) {
|
||||
dominantMonitor.setMessage(message);
|
||||
slaveMonitor.setMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return dominantMonitor.getMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgress(long value) {
|
||||
dominantMonitor.setProgress(value);
|
||||
|
|
|
@ -57,6 +57,11 @@ public class HeadlessTimedTaskMonitor implements TaskMonitor {
|
|||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgress(long value) {
|
||||
// stub
|
||||
|
@ -82,6 +87,11 @@ public class HeadlessTimedTaskMonitor implements TaskMonitor {
|
|||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIndeterminate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkCanceled() throws CancelledException {
|
||||
if (isCancelled()) {
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
/* ###
|
||||
* 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.*;
|
||||
|
||||
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
|
||||
*/
|
||||
class BasicTaskMonitor implements TaskMonitor {
|
||||
|
||||
private WeakSet<CancelledListener> listeners =
|
||||
WeakDataStructureFactory.createCopyOnReadWeakSet();
|
||||
|
||||
private AtomicReference<String> message = new AtomicReference<>();
|
||||
private AtomicLong progress = new AtomicLong();
|
||||
private AtomicLong maxProgress = new AtomicLong();
|
||||
|
||||
private AtomicBoolean cancelEnabled = new AtomicBoolean(true);
|
||||
private AtomicBoolean isCancelled = new AtomicBoolean(false);
|
||||
private AtomicBoolean isIndeterminate = new AtomicBoolean(false);
|
||||
|
||||
@Override
|
||||
public void addCancelledListener(CancelledListener mcl) {
|
||||
listeners.add(mcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCancelledListener(CancelledListener mcl) {
|
||||
listeners.remove(mcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incrementProgress(long incrementAmount) {
|
||||
setProgress(progress.get() + incrementAmount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getProgress() {
|
||||
return progress.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return isCancelled.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkCanceled() throws CancelledException {
|
||||
if (isCancelled.get()) {
|
||||
throw new CancelledException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessage(String message) {
|
||||
this.message.set(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgress(long value) {
|
||||
progress.set(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(long maxValue) {
|
||||
setMaximum(maxValue);
|
||||
setProgress(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaximum(long max) {
|
||||
this.maxProgress.set(max);
|
||||
if (progress.get() > max) {
|
||||
progress.set(max);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIndeterminate(boolean indeterminate) {
|
||||
isIndeterminate.set(indeterminate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelEnabled(boolean enable) {
|
||||
cancelEnabled.set(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelEnabled() {
|
||||
return cancelEnabled.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
boolean wasCancelled = isCancelled.getAndSet(true);
|
||||
if (!wasCancelled) {
|
||||
notifyChangeListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCanceled() {
|
||||
isCancelled.set(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaximum() {
|
||||
return maxProgress.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIndeterminate() {
|
||||
return isIndeterminate.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShowProgressValue(boolean showProgressValue) {
|
||||
// stub
|
||||
}
|
||||
|
||||
private synchronized void notifyChangeListeners() {
|
||||
for (CancelledListener listener : listeners) {
|
||||
listener.cancelled();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ package ghidra.util.task;
|
|||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
|
@ -50,14 +51,13 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
private Runnable shouldCancelRunnable;
|
||||
private boolean taskDone;
|
||||
private JPanel mainPanel;
|
||||
private ChompingBitsAnimationPanel chompingBitsPanel;
|
||||
private JPanel activityPanel;
|
||||
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 */
|
||||
private String newPrimaryMessage;
|
||||
private AtomicReference<String> newMessage = new AtomicReference<>();
|
||||
private SwingUpdateManager messageUpdater =
|
||||
new SwingUpdateManager(() -> setStatusText(newMessage.getAndSet(null)));
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -112,7 +112,7 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
|
||||
private void setup(boolean canCancel, boolean hasProgress) {
|
||||
monitorComponent = new TaskMonitorComponent(false, false);
|
||||
chompingBitsPanel = new ChompingBitsAnimationPanel();
|
||||
activityPanel = new ChompingBitsAnimationPanel();
|
||||
|
||||
setCancelEnabled(canCancel);
|
||||
setRememberLocation(false);
|
||||
|
@ -122,12 +122,7 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
close();
|
||||
dispose();
|
||||
};
|
||||
updatePrimaryMessageRunnable = () -> {
|
||||
setStatusText(newPrimaryMessage);
|
||||
synchronized (TaskDialog.this) {
|
||||
newPrimaryMessage = null;
|
||||
}
|
||||
};
|
||||
|
||||
shouldCancelRunnable = () -> {
|
||||
int currentTaskID = taskID.get();
|
||||
|
||||
|
@ -160,10 +155,9 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
*
|
||||
* @return true if the task should be cancelled
|
||||
*/
|
||||
protected boolean promptToVerifyCancel() {
|
||||
private boolean promptToVerifyCancel() {
|
||||
boolean userSaysYes = OptionDialog.showYesNoDialog(getComponent(), "Cancel?",
|
||||
"Do you really want to cancel \"" + getTitle() + "\"?") == OptionDialog.OPTION_ONE;
|
||||
|
||||
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
|
||||
* called if the dialog has no need to display progress.
|
||||
* Adds the panel that contains the activity panel (e.g., the eating bits animation) to the
|
||||
* dialog. This should only be called if the dialog has no need to display progress.
|
||||
*/
|
||||
private void installActivityDisplay() {
|
||||
SystemUtilities.runIfSwingOrPostSwingLater(() -> {
|
||||
mainPanel.removeAll();
|
||||
mainPanel.add(chompingBitsPanel, BorderLayout.CENTER);
|
||||
mainPanel.add(activityPanel, BorderLayout.CENTER);
|
||||
repack();
|
||||
});
|
||||
}
|
||||
|
@ -203,77 +197,11 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
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
|
||||
protected void cancelCallback() {
|
||||
SwingUtilities.invokeLater(shouldCancelRunnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
synchronized public void setMessage(String str) {
|
||||
boolean invoke = (newPrimaryMessage == null);
|
||||
if (invoke) {
|
||||
newPrimaryMessage = str;
|
||||
SwingUtilities.invokeLater(updatePrimaryMessageRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelEnabled(boolean enable) {
|
||||
monitorComponent.setCancelEnabled(enable);
|
||||
|
@ -300,11 +228,6 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
return taskDone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return monitorComponent.isCancelled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the dialog window centered on the parent window.
|
||||
* Dialog display is delayed if delay greater than zero.
|
||||
|
@ -382,6 +305,71 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
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
|
||||
public synchronized void cancel() {
|
||||
if (monitorComponent.isCancelled()) {
|
||||
|
@ -421,4 +409,8 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
public void removeCancelledListener(CancelledListener listener) {
|
||||
monitorComponent.removeCancelledListener(listener);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// End TaskMonitor Methods
|
||||
//==================================================================================================
|
||||
}
|
||||
|
|
|
@ -16,10 +16,8 @@
|
|||
package ghidra.util.task;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
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
|
||||
|
@ -30,13 +28,6 @@ import ghidra.util.exception.UnableToSwingException;
|
|||
* {@link #TaskLauncher(Task, Component, int, int)}. Alternatively, for simpler uses,
|
||||
* 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>
|
||||
* Most clients of this class should not be concerned with where
|
||||
|
@ -229,37 +220,8 @@ public class TaskLauncher {
|
|||
*/
|
||||
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);
|
||||
if (Swing.isEventDispatchThread()) {
|
||||
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;
|
||||
runner.run();
|
||||
}
|
||||
|
||||
// template method to allow task runner change; used by tests
|
||||
|
|
|
@ -155,6 +155,11 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
|||
startUpdateTimer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String getMessage() {
|
||||
return progressMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setProgress(long value) {
|
||||
if (progress == value) {
|
||||
|
|
|
@ -16,12 +16,13 @@
|
|||
package ghidra.util.task;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import generic.concurrent.GThreadPool;
|
||||
import generic.util.WindowUtilities;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.TaskUtilities;
|
||||
import ghidra.util.*;
|
||||
|
||||
/**
|
||||
* Helper class to launch the given task in a background thread, showing a task dialog if
|
||||
|
@ -29,21 +30,24 @@ import ghidra.util.TaskUtilities;
|
|||
*/
|
||||
class TaskRunner {
|
||||
|
||||
protected Task task;
|
||||
private Task task;
|
||||
private Component parent;
|
||||
private int delayMs;
|
||||
private int dialogWidth;
|
||||
private TaskDialog taskDialog;
|
||||
|
||||
private Thread taskThread;
|
||||
private CancelledListener monitorChangeListener = () -> {
|
||||
if (task.isInterruptible()) {
|
||||
taskThread.interrupt();
|
||||
}
|
||||
if (task.isForgettable()) {
|
||||
taskDialog.close(); // close the dialog and forget about the task
|
||||
closeDialog(); // close the dialog and forget about the task
|
||||
}
|
||||
};
|
||||
|
||||
private AtomicReference<TaskDialog> taskDialog = new AtomicReference<>();
|
||||
private CountDownLatch finished = new CountDownLatch(1);
|
||||
|
||||
TaskRunner(Task task, Component parent, int delayMs, int dialogWidth) {
|
||||
this.task = task;
|
||||
this.parent = parent;
|
||||
|
@ -53,17 +57,35 @@ class TaskRunner {
|
|||
|
||||
void run() {
|
||||
|
||||
// note: we need to be on the Swing thread to create our UI widgets
|
||||
Swing.assertThisIsTheSwingThread(
|
||||
"The Task runner is required to be run from the Swing thread");
|
||||
BasicTaskMonitor internalMonitor = new BasicTaskMonitor();
|
||||
WrappingTaskMonitor monitor = new WrappingTaskMonitor(internalMonitor);
|
||||
startBackgroundThread(monitor);
|
||||
|
||||
this.taskDialog = buildTaskDialog(parent);
|
||||
|
||||
startBackgroundThread(taskDialog);
|
||||
|
||||
taskDialog.show(Math.max(delayMs, 0));
|
||||
showDialogIfSwingOrShowLaterAndWait(monitor);
|
||||
}
|
||||
|
||||
private void showDialogIfSwingOrShowLaterAndWait(WrappingTaskMonitor monitor) {
|
||||
|
||||
Swing.runIfSwingOrRunLater(() -> showTaskDialog(monitor));
|
||||
waitForModalIfNotSwing();
|
||||
}
|
||||
|
||||
private void waitForModalIfNotSwing() {
|
||||
if (Swing.isSwingThread() || !task.isModal()) {
|
||||
// if this is the Swing thread, then the work is already done at this point; otherwise,
|
||||
// the task is not modal, so do not block
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
finished.await();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Msg.debug(this, "Task Launcher unexpectedly interrupted waiting for task thread", e);
|
||||
}
|
||||
}
|
||||
|
||||
// protected to allow for dependency injection
|
||||
protected TaskDialog buildTaskDialog(Component comp) {
|
||||
|
||||
//
|
||||
|
@ -71,16 +93,30 @@ class TaskRunner {
|
|||
// 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);
|
||||
TaskDialog dialog = createTaskDialog(comp);
|
||||
dialog.setMinimumSize(dialogWidth, 0);
|
||||
|
||||
if (task.isInterruptible() || task.isForgettable()) {
|
||||
taskDialog.addCancelledListener(monitorChangeListener);
|
||||
dialog.addCancelledListener(monitorChangeListener);
|
||||
}
|
||||
|
||||
taskDialog.setStatusJustification(task.getStatusTextAlignment());
|
||||
dialog.setStatusJustification(task.getStatusTextAlignment());
|
||||
|
||||
return taskDialog;
|
||||
return dialog;
|
||||
}
|
||||
|
||||
private void showTaskDialog(WrappingTaskMonitor monitor) {
|
||||
|
||||
Swing.assertThisIsTheSwingThread("Must be on the Swing thread build the Task Dialog");
|
||||
|
||||
if (finished.getCount() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
TaskDialog dialog = buildTaskDialog(parent);
|
||||
taskDialog.set(dialog);
|
||||
monitor.setDelegate(dialog); // initialize the dialog to the current state of the monitor
|
||||
dialog.show(Math.max(delayMs, 0));
|
||||
}
|
||||
|
||||
private void startBackgroundThread(TaskMonitor monitor) {
|
||||
|
@ -93,11 +129,31 @@ class TaskRunner {
|
|||
Executor executor = pool.getExecutor();
|
||||
executor.execute(() -> {
|
||||
Thread.currentThread().setName(name);
|
||||
task.monitoredRun(monitor);
|
||||
taskDialog.taskProcessed();
|
||||
|
||||
try {
|
||||
task.monitoredRun(monitor);
|
||||
}
|
||||
finally {
|
||||
taskFinished();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void taskFinished() {
|
||||
finished.countDown();
|
||||
TaskDialog dialog = taskDialog.get();
|
||||
if (dialog != null) {
|
||||
dialog.taskProcessed();
|
||||
}
|
||||
}
|
||||
|
||||
private void closeDialog() {
|
||||
TaskDialog dialog = taskDialog.get();
|
||||
if (dialog != null) {
|
||||
dialog.close();
|
||||
}
|
||||
}
|
||||
|
||||
private TaskDialog createTaskDialog(Component comp) {
|
||||
Component currentParent = comp;
|
||||
if (currentParent != null) {
|
||||
|
|
|
@ -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 {
|
||||
threadsFinished.await(2, TimeUnit.SECONDS);
|
||||
}
|
||||
|
@ -104,11 +112,6 @@ public class AbstractTaskTest extends AbstractDockingTest {
|
|||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSwingTimeoutInSeconds() {
|
||||
return 1; // speed-up for tests
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runInThisBackgroundThread(Task task) {
|
||||
didRunInBackground.set(true);
|
||||
|
|
|
@ -62,7 +62,7 @@ public class TaskDialogTest extends AbstractTaskTest {
|
|||
waitForTask();
|
||||
|
||||
assertFalse(dialogSpy.wasShown());
|
||||
assertSwingThreadFinishedBeforeTask();
|
||||
assertNoDialogShown();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -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,
|
||||
* 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.
|
||||
*
|
||||
* @param <I> the input type
|
||||
* @param <R> the output type
|
||||
*/
|
||||
class FutureTaskMonitor<I, R> extends FutureTask<R> implements TaskMonitor {
|
||||
|
||||
private final ConcurrentQ<I, R> queue;
|
||||
private final I item;
|
||||
private final long id;
|
||||
private volatile String lastMessage;
|
||||
private volatile long currentProgress;
|
||||
private volatile long maxProgress;
|
||||
private volatile CancelledListener cancelledListener;
|
||||
private volatile boolean isIndeterminate;
|
||||
|
||||
FutureTaskMonitor(ConcurrentQ<I, R> queue, Callable<R> callable, I item, long id) {
|
||||
super(callable);
|
||||
|
@ -103,6 +108,11 @@ class FutureTaskMonitor<I, R> extends FutureTask<R> implements TaskMonitor {
|
|||
queue.progressMessageChanged(id, item, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return lastMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(long max) {
|
||||
currentProgress = 0;
|
||||
|
@ -123,9 +133,15 @@ class FutureTaskMonitor<I, R> extends FutureTask<R> implements TaskMonitor {
|
|||
|
||||
@Override
|
||||
public void setIndeterminate(boolean indeterminate) {
|
||||
this.isIndeterminate = indeterminate;
|
||||
queue.progressModeChanged(id, item, indeterminate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIndeterminate() {
|
||||
return isIndeterminate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getProgress() {
|
||||
return currentProgress;
|
||||
|
|
|
@ -67,6 +67,11 @@ public class TaskMonitorAdapter implements TaskMonitor {
|
|||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgress(long value) {
|
||||
// do nothing
|
||||
|
@ -105,6 +110,11 @@ public class TaskMonitorAdapter implements TaskMonitor {
|
|||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIndeterminate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setCancelEnabled(boolean enable) {
|
||||
cancelEnabled = enable;
|
||||
|
@ -134,6 +144,8 @@ public class TaskMonitorAdapter implements TaskMonitor {
|
|||
}
|
||||
cancelled = false;
|
||||
}
|
||||
|
||||
// TODO this seems like a mistake, to notify of 'cancelled' when clearning
|
||||
notifyChangeListeners();
|
||||
}
|
||||
|
||||
|
|
|
@ -136,6 +136,11 @@ public class TaskMonitorSplitter {
|
|||
parent.setIndeterminate(indeterminate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIndeterminate() {
|
||||
return parent.isIndeterminate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(long newMax) {
|
||||
setMaximum(newMax);
|
||||
|
@ -152,7 +157,11 @@ public class TaskMonitorSplitter {
|
|||
@Override
|
||||
public void setMessage(String message) {
|
||||
parent.setMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return parent.getMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -139,6 +139,11 @@ public class TimeoutTaskMonitor implements TaskMonitor {
|
|||
delegate.setMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return delegate.getMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgress(long value) {
|
||||
delegate.setProgress(value);
|
||||
|
@ -164,6 +169,11 @@ public class TimeoutTaskMonitor implements TaskMonitor {
|
|||
delegate.setIndeterminate(indeterminate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIndeterminate() {
|
||||
return delegate.isIndeterminate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkCanceled() throws CancelledException {
|
||||
if (didTimeout()) {
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package ghidra.util.task;
|
||||
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
||||
/**
|
||||
|
@ -24,7 +26,9 @@ import ghidra.util.exception.CancelledException;
|
|||
*/
|
||||
public class WrappingTaskMonitor implements TaskMonitor {
|
||||
|
||||
protected final TaskMonitor delegate;
|
||||
private WeakSet<CancelledListener> listeners =
|
||||
WeakDataStructureFactory.createCopyOnReadWeakSet();
|
||||
protected TaskMonitor delegate;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -35,6 +39,25 @@ public class WrappingTaskMonitor implements TaskMonitor {
|
|||
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 void setDelegate(TaskMonitor newDelegate) {
|
||||
newDelegate.setMaximum(delegate.getMaximum());
|
||||
newDelegate.setProgress(delegate.getProgress());
|
||||
newDelegate.setMessage(delegate.getMessage());
|
||||
newDelegate.setIndeterminate(delegate.isIndeterminate());
|
||||
for (CancelledListener l : listeners) {
|
||||
newDelegate.addCancelledListener(l);
|
||||
delegate.removeCancelledListener(l);
|
||||
}
|
||||
|
||||
this.delegate = newDelegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return delegate.isCancelled();
|
||||
|
@ -50,6 +73,11 @@ public class WrappingTaskMonitor implements TaskMonitor {
|
|||
delegate.setMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return delegate.getMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgress(long value) {
|
||||
delegate.setProgress(value);
|
||||
|
@ -75,6 +103,11 @@ public class WrappingTaskMonitor implements TaskMonitor {
|
|||
delegate.setIndeterminate(indeterminate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIndeterminate() {
|
||||
return delegate.isIndeterminate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkCanceled() throws CancelledException {
|
||||
delegate.checkCanceled();
|
||||
|
@ -97,11 +130,13 @@ public class WrappingTaskMonitor implements TaskMonitor {
|
|||
|
||||
@Override
|
||||
public void addCancelledListener(CancelledListener listener) {
|
||||
listeners.add(listener);
|
||||
delegate.addCancelledListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCancelledListener(CancelledListener listener) {
|
||||
listeners.remove(listener);
|
||||
delegate.removeCancelledListener(listener);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ class LockingTaskMonitor implements TaskMonitor {
|
|||
* be done in a try/finally block to avoid accidentally locking the
|
||||
* domain object indefinitely.
|
||||
* @param dobj domain object
|
||||
* @param hasProgress
|
||||
* @param hasProgress true if this monitorhas progress
|
||||
* @param title task 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)
|
||||
*/
|
||||
|
@ -165,6 +170,11 @@ class LockingTaskMonitor implements TaskMonitor {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIndeterminate() {
|
||||
return indeterminate;
|
||||
}
|
||||
|
||||
/*
|
||||
* @see ghidra.util.task.TaskMonitor#setCancelEnabled(boolean)
|
||||
*/
|
||||
|
@ -206,9 +216,6 @@ class LockingTaskMonitor implements TaskMonitor {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @see ghidra.util.task.TaskMonitor#checkCanceled()
|
||||
*/
|
||||
@Override
|
||||
public void checkCanceled() throws CancelledException {
|
||||
if (isCancelled()) {
|
||||
|
@ -216,17 +223,11 @@ class LockingTaskMonitor implements TaskMonitor {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.util.task.TaskMonitor#incrementProgress(int)
|
||||
*/
|
||||
@Override
|
||||
public void incrementProgress(long incrementAmount) {
|
||||
setProgress(curProgress + incrementAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.util.task.TaskMonitor#getProgress()
|
||||
*/
|
||||
@Override
|
||||
public long getProgress() {
|
||||
return curProgress;
|
||||
|
|
|
@ -102,6 +102,11 @@ public class GTaskMonitor implements TaskMonitor, CancelledListener {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIndeterminate() {
|
||||
return indeterminate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkCanceled() throws CancelledException {
|
||||
if (isCancelled) {
|
||||
|
@ -163,6 +168,7 @@ public class GTaskMonitor implements TaskMonitor, CancelledListener {
|
|||
return showProgressValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ public class Swing {
|
|||
|
||||
private static final String SWING_TIMEOUT_SECONDS_PROPERTY =
|
||||
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() {
|
||||
String timeoutString = System.getProperty(SWING_TIMEOUT_SECONDS_PROPERTY,
|
||||
|
@ -70,6 +70,14 @@ public class Swing {
|
|||
return SwingUtilities.isEventDispatchThread();
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method for {@link #isEventDispatchThread()}
|
||||
* @return true if this is the Swing thread
|
||||
*/
|
||||
public static boolean isSwingThread() {
|
||||
return isEventDispatchThread();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until AWT event queue (Swing) has been flushed and no more (to a point) events
|
||||
* are pending.
|
||||
|
|
|
@ -33,7 +33,11 @@ class StubTaskMonitor implements TaskMonitor {
|
|||
@Override
|
||||
public void setMessage(String message) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -64,6 +68,11 @@ class StubTaskMonitor implements TaskMonitor {
|
|||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIndeterminate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkCanceled() throws CancelledException {
|
||||
// stub
|
||||
|
|
|
@ -56,6 +56,12 @@ public interface TaskMonitor {
|
|||
*/
|
||||
public void setMessage(String message);
|
||||
|
||||
/**
|
||||
* Gets the last set message of this monitor
|
||||
* @return the message
|
||||
*/
|
||||
public String getMessage();
|
||||
|
||||
/**
|
||||
* Sets the current progress value
|
||||
* @param value progress value
|
||||
|
@ -91,6 +97,12 @@ public interface TaskMonitor {
|
|||
*/
|
||||
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
|
||||
* @throws CancelledException if monitor has been cancelled
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue