GT-2861 - Task Monitor - removed recently added Task Monitor Service due

to conceptual issues; fixed bug in TaskLauncher that caused deadlock;
updated ImporterUtilities usage of TaskLauncher to trigger task dialog
This commit is contained in:
dragonmacher 2019-05-14 08:54:17 -04:00
parent 2108a5ed4c
commit a94c6efe68
21 changed files with 413 additions and 1452 deletions

View file

@ -67,7 +67,6 @@ public class DialogComponentProvider
protected JButton dismissButton;
private boolean isAlerting;
private JLabel statusLabel;
private JLabel subStatusLabel;
private JPanel statusProgPanel; // contains status panel and progress panel
private Timer showTimer;
private TaskScheduler taskScheduler;
@ -609,11 +608,6 @@ public class DialogComponentProvider
public void setStatusText(String text) {
setStatusText(text, MessageType.INFO);
}
@Override
public void setSubStatusText(String text) {
setSubStatusText(text, MessageType.INFO);
}
/**
* Sets the text in the dialog's status line using the specified message type to control
@ -627,24 +621,12 @@ public class DialogComponentProvider
setStatusText(message, type, false);
}
@Override
public void setSubStatusText(String message, MessageType type) {
setSubStatusText(message, type, false);
}
@Override
public void setStatusText(String message, MessageType type, boolean alert) {
String text = StringUtils.isBlank(message) ? " " : message;
SystemUtilities.runIfSwingOrPostSwingLater(() -> doSetStatusText(text, type, alert));
}
@Override
public void setSubStatusText(String message, MessageType type, boolean alert) {
String text = StringUtils.isBlank(message) ? " " : message;
SystemUtilities.runIfSwingOrPostSwingLater(() -> doSetSubStatusText(text, type, alert));
}
private void doSetStatusText(String text, MessageType type, boolean alert) {
@ -659,14 +641,12 @@ public class DialogComponentProvider
alertMessage();
}
}
private void doSetSubStatusText(String text, MessageType type, boolean alert) {
SystemUtilities.assertThisIsTheSwingThread(
"Setting text must be performed on the Swing thread");
subStatusLabel.setText(text);
subStatusLabel.setForeground(getStatusColor(type));
updateStatusToolTip();
if (alert) {
@ -711,13 +691,11 @@ public class DialogComponentProvider
// normal Swing mechanism may not have yet happened).
mainPanel.validate();
statusLabel.setVisible(false); // disable painting in this dialog so we don't see double
subStatusLabel.setVisible(false);
Animator animator = AnimationUtils.pulseComponent(statusLabel, 1);
animator.addTarget(new TimingTargetAdapter() {
@Override
public void end() {
statusLabel.setVisible(true);
subStatusLabel.setVisible(true);
alertFinishedCallback.call();
isAlerting = false;
}
@ -828,7 +806,6 @@ public class DialogComponentProvider
public void clearStatusText() {
SystemUtilities.runIfSwingOrPostSwingLater(() -> {
statusLabel.setText(" ");
subStatusLabel.setText(" ");
updateStatusToolTip();
});
}
@ -842,15 +819,6 @@ public class DialogComponentProvider
return statusLabel.getText();
}
/**
* Returns the secondary status message
*
* @return the secondary status message
*/
public String getSubStatusText() {
return subStatusLabel.getText();
}
protected JLabel getStatusLabel() {
return statusLabel;
}
@ -941,23 +909,13 @@ public class DialogComponentProvider
updateStatusToolTip();
}
});
subStatusLabel = new JLabel(" ");
subStatusLabel.setName("subStatusLabel");
subStatusLabel.setHorizontalAlignment(SwingConstants.CENTER);
subStatusLabel.setForeground(Color.blue);
subStatusLabel.setFont(subStatusLabel.getFont().deriveFont(Font.ITALIC));
subStatusLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 3, 5));
subStatusLabel.setFont(subStatusLabel.getFont().deriveFont(9.0f));
// use a strut panel so the size of the message area does not change if we make
// the message label not visible
int height =
statusLabel.getPreferredSize().height + subStatusLabel.getPreferredSize().height + 5;
int height = statusLabel.getPreferredSize().height;
panel.add(Box.createVerticalStrut(height), BorderLayout.WEST);
panel.add(statusLabel, BorderLayout.CENTER);
panel.add(subStatusLabel, BorderLayout.SOUTH);
return panel;
}

View file

@ -1,180 +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.Objects;
import ghidra.util.exception.CancelledException;
/**
* {@link TaskMonitor} that restricts users from being able to update the progress bar. The class
* is initialized with another, fully-featured monitor and forwards all requests to it,
* but squashes calls to methods that are not allowed.
* <p>
* Note: Two instances of this class are deemed equal if they have the same {@link #parentMonitor},
* hence the override of {@link #hashCode()} and {@link #equals(Object)}.
*/
public class SecondaryTaskMonitor implements TaskMonitor {
private TaskMonitor parentMonitor;
/**
* Constructor
*
* @param parentMonitor the fully-functional task monitor this is based off of
*/
public SecondaryTaskMonitor(TaskMonitor parentMonitor) {
this.parentMonitor = parentMonitor;
}
/**
* Overridden to ensure that clients who have this type of monitor will only update the
* secondary message when using this method
*
* @param message the message string to display
*/
@Override
public void setMessage(String message) {
if (parentMonitor instanceof TaskDialog) {
((TaskDialog) parentMonitor).setSecondaryMessage(message);
return;
}
parentMonitor.setMessage(message);
}
@Override
public void setCancelEnabled(boolean enable) {
parentMonitor.setCancelEnabled(enable);
}
@Override
public void setInitialized(boolean init) {
parentMonitor.setInitialized(init);
}
/**
* Secondary monitors should not be able to reset progress or revert back
* to an uninitialized state; hence the override.
*/
@Override
public void finished() {
synchronized (this) {
setMessage("");
}
}
@Override
public boolean isCancelEnabled() {
return parentMonitor.isCancelEnabled();
}
@Override
public boolean isCancelled() {
return parentMonitor.isCancelled();
}
@Override
public synchronized void cancel() {
parentMonitor.cancel();
}
@Override
public synchronized void clearCanceled() {
parentMonitor.clearCanceled();
}
@Override
public void checkCanceled() throws CancelledException {
parentMonitor.checkCanceled();
}
@Override
public void addCancelledListener(CancelledListener listener) {
parentMonitor.addCancelledListener(listener);
}
@Override
public void removeCancelledListener(CancelledListener listener) {
parentMonitor.removeCancelledListener(listener);
}
@Override
public long getMaximum() {
return parentMonitor.getMaximum();
}
@Override
public long getProgress() {
return parentMonitor.getProgress();
}
@Override
public void setShowProgressValue(boolean showProgressValue) {
// squash
}
@Override
public void setProgress(long value) {
// squash
}
@Override
public void initialize(long max) {
// squash
}
@Override
public void setMaximum(long max) {
// squash
}
@Override
public void setIndeterminate(boolean indeterminate) {
// squash
}
@Override
public void incrementProgress(long incrementAmount) {
// squash
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((parentMonitor == null) ? 0 : parentMonitor.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
SecondaryTaskMonitor other = (SecondaryTaskMonitor) obj;
if (!Objects.equals(parentMonitor, other.parentMonitor)) {
return false;
}
return true;
}
}

View file

@ -42,6 +42,17 @@ import util.CollectionUtils;
* .setStatusTextAlignment(SwingConstants.LEADING)
* .launchModal();
* </pre>
*
* Or,
*
* <pre>
* TaskBuilder.withRunnable(monitor -> doWork(parameter, monitor))
* .setTitle("Task Title")
* .setHasProgress(true)
* .setCanCancel(true)
* .setStatusTextAlignment(SwingConstants.LEADING)
* .launchModal();
* </pre>
*/
public class TaskBuilder {
@ -57,7 +68,25 @@ public class TaskBuilder {
private int statusTextAlignment = SwingConstants.CENTER;
/**
* Constructor.
* A convenience method to start a builder using the given runnable. After calling this
* method you are still required to call {@link #setTitle(String)}.
*
* <p>This method allows for a more attractive fluent API usage than does the constructor
* (see the javadoc header).
*
* @param r the runnable
* @return this builder
*/
public static TaskBuilder withRunnable(MonitoredRunnable r) {
return new TaskBuilder(r);
}
private TaskBuilder(MonitoredRunnable r) {
this.runnable = Objects.requireNonNull(r);
}
/**
* Constructor
*
* @param title the required title for your task. This will appear as the title of the
* task dialog
@ -68,6 +97,18 @@ public class TaskBuilder {
this.runnable = Objects.requireNonNull(runnable);
}
/**
* Sets the title of this task. The title must be set before calling any of the
* <code>launch</code> methods.
*
* @param title the title
* @return this builder
*/
public TaskBuilder setTitle(String title) {
this.title = Objects.requireNonNull(title);
return this;
}
/**
* Sets whether this task reports progress. The default is <tt>true</tt>.
*
@ -149,6 +190,8 @@ public class TaskBuilder {
* Launches the task built by this builder, using a blocking modal dialog.
*/
public void launchModal() {
validate();
boolean isModal = true;
Task t = new TaskBuilderTask(isModal);
int delay = getDelay(launchDelay, isModal);
@ -160,6 +203,8 @@ public class TaskBuilder {
* @return the launcher that launched the task
*/
public TaskLauncher launchNonModal() {
validate();
boolean isModal = false;
Task t = new TaskBuilderTask(isModal);
int delay = getDelay(launchDelay, isModal);
@ -167,6 +212,12 @@ public class TaskBuilder {
return launcher;
}
private void validate() {
if (title == null) {
throw new NullPointerException("Task title cannot be null");
}
}
private static int getDelay(int userDelay, boolean isModal) {
if (userDelay >= 0) {
return userDelay;

View file

@ -17,7 +17,6 @@ package ghidra.util.task;
import java.awt.BorderLayout;
import java.awt.Component;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import javax.swing.*;
@ -38,10 +37,10 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
/** Timer used to give the task a chance to complete */
private static final int SLEEPY_TIME = 10;
/** Amount of time to wait before showing the monitor dialog */
private final static int MAX_DELAY = 200000;
public final static int DEFAULT_WIDTH = 275;
private Timer showTimer;
@ -57,24 +56,9 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
/** Runnable that updates the primary message label in the dialog */
private Runnable updatePrimaryMessageRunnable;
/** Runnable that updates the secondary message label in the dialog */
private Runnable updateSecondaryMessageRunnable;
/** If not null, then the value of the string has yet to be rendered */
private String newPrimaryMessage;
/** If not null, then the value of the string has yet to be rendered */
private String newSecondaryMessage;
/**
* Indicates if this monitor has been initialized for progress updates. If this value
* is set to true, the {@link TaskMonitorService} will not return the monitor to
* another caller (only one client should be able to update progress at a time).
*/
private AtomicBoolean initialized = new AtomicBoolean(false);
private SecondaryTaskMonitor secondaryTaskMonitor;
/**
* Constructor
*
@ -108,7 +92,7 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
public TaskDialog(String title, boolean canCancel, boolean isModal, boolean hasProgress) {
this(null, title, isModal, canCancel, hasProgress);
}
/**
* Constructor
*
@ -126,20 +110,10 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
setup(canCancel, hasProgress);
}
@Override
public boolean isInitialized() {
return initialized.get();
}
@Override
public void setInitialized(boolean init) {
this.initialized.set(init);
}
private void setup(boolean canCancel, boolean hasProgress) {
monitorComponent = new TaskMonitorComponent(false, false);
chompingBitsPanel = new ChompingBitsAnimationPanel();
setCancelEnabled(canCancel);
setRememberLocation(false);
setRememberSize(false);
@ -154,12 +128,6 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
newPrimaryMessage = null;
}
};
updateSecondaryMessageRunnable = () -> {
setSubStatusText(newSecondaryMessage);
synchronized (TaskDialog.this) {
newSecondaryMessage = null;
}
};
shouldCancelRunnable = () -> {
int currentTaskID = taskID.get();
@ -171,7 +139,7 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
mainPanel = new JPanel(new BorderLayout());
addWorkPanel(mainPanel);
if (hasProgress) {
installProgressMonitor();
}
@ -306,19 +274,6 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
}
}
/**
* Updates the secondary message on the task monitor
*
* @param str the string to update
*/
synchronized public void setSecondaryMessage(String str) {
boolean invoke = (newSecondaryMessage == null);
if (invoke) {
newSecondaryMessage = str;
SwingUtilities.invokeLater(updateSecondaryMessageRunnable);
}
}
@Override
public void setCancelEnabled(boolean enable) {
monitorComponent.setCancelEnabled(enable);
@ -386,7 +341,6 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
// only to show a progress dialog if enough time has elapsed.
//
GTimer.scheduleRunnable(delay, () -> {
if (isCompleted()) {
return;
}
@ -467,12 +421,4 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
public void removeCancelledListener(CancelledListener listener) {
monitorComponent.removeCancelledListener(listener);
}
@Override
public synchronized TaskMonitor getSecondaryMonitor() {
if (secondaryTaskMonitor == null) {
secondaryTaskMonitor = new SecondaryTaskMonitor(this);
}
return secondaryTaskMonitor;
}
}

View file

@ -20,7 +20,8 @@ import java.awt.Component;
import javax.swing.SwingUtilities;
import generic.util.WindowUtilities;
import ghidra.util.*;
import ghidra.util.Msg;
import ghidra.util.TaskUtilities;
/**
* Class to initiate a Task in a new Thread, and to show a progress dialog that indicates
@ -285,16 +286,8 @@ public class TaskLauncher {
private TaskDialog buildTaskDialog(Component comp, int dialogWidth) {
//
// This class may be used by background threads. Make sure that our GUI creation is
// on the Swing thread to prevent exceptions while painting (as seen when using the
// Nimbus Look and Feel).
//
SystemUtilities.runSwingNow(() -> {
taskDialog = createTaskDialog(comp);
taskDialog.setMinimumSize(dialogWidth, 0);
});
taskDialog = createTaskDialog(comp);
taskDialog.setMinimumSize(dialogWidth, 0);
if (task.isInterruptible() || task.isForgettable()) {
taskDialog.addCancelledListener(monitorChangeListener);