mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
GP-4270: Minor fixes
This commit is contained in:
parent
b9f914d57c
commit
1cf7803d88
22 changed files with 295 additions and 183 deletions
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,11 +15,11 @@
|
|||
*/
|
||||
package ghidra.app.decompiler;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import generic.concurrent.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class DecompilerDisposer {
|
||||
private static String THREAD_POOL_NAME = "Decompiler Disposer";
|
||||
private static ConcurrentQ<AbstractDisposable, AbstractDisposable> queue;
|
||||
|
@ -38,7 +37,7 @@ public class DecompilerDisposer {
|
|||
/**
|
||||
* Disposes the given Process and related streams from a background thread. This is necessary
|
||||
* due to a low-probability deadlock that occurs in the JVM.
|
||||
*
|
||||
*
|
||||
* @param process The process to destroy.
|
||||
* @param ouputStream The output stream to close
|
||||
* @param inputStream The input stream to close
|
||||
|
@ -54,23 +53,24 @@ public class DecompilerDisposer {
|
|||
* <p>
|
||||
* Note:<br>
|
||||
* A class to handle the rare case where the {@link DecompInterface}'s
|
||||
* synchronized methods are blocking
|
||||
* synchronized methods are blocking
|
||||
* while a decompile operation has died and maintained the lock. In that scenario, calling
|
||||
* dispose on this class will eventually try to enter a synchronized method that will
|
||||
* dispose on this class will eventually try to enter a synchronized method that will
|
||||
* remain blocked forever.
|
||||
* <p>
|
||||
* I examined the uses of dispose() on the {@link DecompInterface} and
|
||||
* I examined the uses of dispose() on the {@link DecompInterface} and
|
||||
* determined that calling dispose() is a
|
||||
* final operation, which means that you don't have to wait. Further, after calling
|
||||
* dispose() on this class, you should no longer use it.
|
||||
* @param decompiler the decompiler
|
||||
*/
|
||||
public static void dispose(DecompInterface decompiler) {
|
||||
DecompInterfaceDisposable disposable = new DecompInterfaceDisposable(decompiler);
|
||||
queue.add(disposable);
|
||||
}
|
||||
|
||||
private static class DisposeCallback implements
|
||||
QCallback<AbstractDisposable, AbstractDisposable> {
|
||||
private static class DisposeCallback
|
||||
implements QCallback<AbstractDisposable, AbstractDisposable> {
|
||||
@Override
|
||||
public AbstractDisposable process(AbstractDisposable disposable, TaskMonitor monitor) {
|
||||
disposable.dispose();
|
||||
|
@ -87,7 +87,8 @@ public class DecompilerDisposer {
|
|||
private OutputStream ouputStream;
|
||||
private InputStream inputStream;
|
||||
|
||||
RuntimeProcessDisposable(Process process, OutputStream ouputStream, InputStream inputStream) {
|
||||
RuntimeProcessDisposable(Process process, OutputStream ouputStream,
|
||||
InputStream inputStream) {
|
||||
this.process = process;
|
||||
this.ouputStream = ouputStream;
|
||||
this.inputStream = inputStream;
|
||||
|
|
|
@ -20,8 +20,10 @@ import java.awt.Point;
|
|||
import java.awt.event.MouseEvent;
|
||||
import java.util.Objects;
|
||||
|
||||
import generic.json.Json;
|
||||
|
||||
/**
|
||||
* A class that holds information used to show a popup menu
|
||||
* A class that holds information used to show a popup menu
|
||||
*/
|
||||
public class PopupMenuContext {
|
||||
|
||||
|
@ -58,4 +60,9 @@ public class PopupMenuContext {
|
|||
}
|
||||
return component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Json.toString(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,10 +74,10 @@ public class DefaultDropDownSelectionDataModel<T> implements DropDownTextFieldDa
|
|||
|
||||
@Override
|
||||
public int getIndexOfFirstMatchingEntry(List<T> list, String text) {
|
||||
// The data are sorted such that lower-case is before upper-case and smaller length
|
||||
// matches come before longer matches. If we ever find a case-sensitive exact match,
|
||||
// use that. Otherwise, keep looking for a case-insensitive exact match. The
|
||||
// case-insensitive match is preferred over a non-matching item. Once we get to a
|
||||
// The data are sorted such that lower-case is before upper-case and smaller length
|
||||
// matches come before longer matches. If we ever find a case-sensitive exact match,
|
||||
// use that. Otherwise, keep looking for a case-insensitive exact match. The
|
||||
// case-insensitive match is preferred over a non-matching item. Once we get to a
|
||||
// non-matching item, we can quit.
|
||||
int lastPreferredMatchIndex = -1;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
|
@ -118,7 +118,7 @@ public class DefaultDropDownSelectionDataModel<T> implements DropDownTextFieldDa
|
|||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
//==================================================================================================
|
||||
|
||||
private class ObjectStringComparator implements Comparator<Object> {
|
||||
Comparator<String> stringComparator = new CaseInsensitiveDuplicateStringComparator();
|
||||
|
|
|
@ -33,7 +33,6 @@ public class ListSelectionDialog<T> extends DialogComponentProvider {
|
|||
|
||||
private DropDownSelectionTextField<T> field;
|
||||
protected boolean cancelled;
|
||||
private RowObjectTableModel<T> userTableModel;
|
||||
private DataToStringConverter<T> searchConverter;
|
||||
private DataToStringConverter<T> descriptionConverter;
|
||||
private List<T> data;
|
||||
|
@ -61,8 +60,12 @@ public class ListSelectionDialog<T> extends DialogComponentProvider {
|
|||
this.data = data;
|
||||
this.searchConverter = searchConverter;
|
||||
this.descriptionConverter = descriptionConverter;
|
||||
|
||||
// Use a separate list for the drop down widget, since it needs to sort its data and we do
|
||||
// not want to change the client data sort.
|
||||
List<T> dropDownData = new ArrayList<>(data);
|
||||
DefaultDropDownSelectionDataModel<T> model = new DefaultDropDownSelectionDataModel<>(
|
||||
new ArrayList<>(data), searchConverter, descriptionConverter) {
|
||||
dropDownData, searchConverter, descriptionConverter) {
|
||||
|
||||
// overridden to return all data for an empty search; this lets the down-arrow
|
||||
// show the full list
|
||||
|
@ -151,14 +154,18 @@ public class ListSelectionDialog<T> extends DialogComponentProvider {
|
|||
}
|
||||
|
||||
private RowObjectTableModel<T> getTableModel() {
|
||||
if (userTableModel != null) {
|
||||
return userTableModel;
|
||||
}
|
||||
|
||||
return new DefaultTableModel();
|
||||
return new DefaultTableModel(data);
|
||||
}
|
||||
|
||||
private class DefaultTableModel extends AbstractGTableModel<T> {
|
||||
|
||||
private List<T> modelData;
|
||||
|
||||
DefaultTableModel(List<T> modelData) {
|
||||
// copy the data so that a call to dispose() will not clear the data in the outer class
|
||||
this.modelData = new ArrayList<>(modelData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int columnIndex) {
|
||||
if (columnIndex == 0) {
|
||||
|
@ -184,7 +191,7 @@ public class ListSelectionDialog<T> extends DialogComponentProvider {
|
|||
|
||||
@Override
|
||||
public List<T> getModelData() {
|
||||
return data;
|
||||
return modelData;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -29,8 +29,6 @@ import utilities.util.reflection.ReflectionUtilities;
|
|||
* determines the appropriate cell object for use by the table column this field represents. It can
|
||||
* then return the appropriate object to display in the table cell for the indicated row object.
|
||||
*
|
||||
* Implementations of this interface must provide a public default constructor.
|
||||
*
|
||||
* @param <ROW_TYPE> The row object class supported by this column
|
||||
* @param <COLUMN_TYPE> The column object class supported by this column
|
||||
* @param <DATA_SOURCE> The object class type that will be passed to see
|
||||
|
@ -95,8 +93,8 @@ public abstract class AbstractDynamicTableColumn<ROW_TYPE, COLUMN_TYPE, DATA_SOU
|
|||
public Class<COLUMN_TYPE> getColumnClass() {
|
||||
@SuppressWarnings("rawtypes")
|
||||
Class<? extends AbstractDynamicTableColumn> implementationClass = getClass();
|
||||
List<Class<?>> typeArguments = ReflectionUtilities.getTypeArguments(
|
||||
AbstractDynamicTableColumn.class, implementationClass);
|
||||
List<Class<?>> typeArguments = ReflectionUtilities
|
||||
.getTypeArguments(AbstractDynamicTableColumn.class, implementationClass);
|
||||
return (Class<COLUMN_TYPE>) typeArguments.get(1);
|
||||
}
|
||||
|
||||
|
@ -106,8 +104,8 @@ public abstract class AbstractDynamicTableColumn<ROW_TYPE, COLUMN_TYPE, DATA_SOU
|
|||
public Class<ROW_TYPE> getSupportedRowType() {
|
||||
@SuppressWarnings("rawtypes")
|
||||
Class<? extends AbstractDynamicTableColumn> implementationClass = getClass();
|
||||
List<Class<?>> typeArguments = ReflectionUtilities.getTypeArguments(
|
||||
AbstractDynamicTableColumn.class, implementationClass);
|
||||
List<Class<?>> typeArguments = ReflectionUtilities
|
||||
.getTypeArguments(AbstractDynamicTableColumn.class, implementationClass);
|
||||
return (Class<ROW_TYPE>) typeArguments.get(0);
|
||||
}
|
||||
|
||||
|
@ -193,7 +191,7 @@ public abstract class AbstractDynamicTableColumn<ROW_TYPE, COLUMN_TYPE, DATA_SOU
|
|||
return getIdentifier().hashCode();
|
||||
}
|
||||
|
||||
// Note: this method is here because the default 'identifier' must be lazy loaded, as
|
||||
// Note: this method is here because the default 'identifier' must be lazy loaded, as
|
||||
// at construction time not all the variables needed are available.
|
||||
private String getIdentifier() {
|
||||
/*
|
||||
|
@ -202,7 +200,7 @@ public abstract class AbstractDynamicTableColumn<ROW_TYPE, COLUMN_TYPE, DATA_SOU
|
|||
-The case where 2 different column classes share the same column header value
|
||||
-The case where a single column class is used repeatedly, with a different
|
||||
column header value each time
|
||||
|
||||
|
||||
Thus, to be unique, we need to combine both the class name and the column header
|
||||
value. The only time this may be an issue is if the column header value changes
|
||||
dynamically--not sure if this actually happens anywhere in our system. If it did,
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -20,14 +20,18 @@ import ghidra.framework.plugintool.ServiceProvider;
|
|||
|
||||
/**
|
||||
* This class is meant to be used by DynamicTableColumn implementations that do not care about
|
||||
* the DATA_SOURCE parameter of DynamicTableColumn. This class will stub the default
|
||||
* the DATA_SOURCE parameter of DynamicTableColumn. This class will stub the default
|
||||
* {@link #getValue(Object, Settings, Object, ServiceProvider)} method and
|
||||
* call a version of the method that does not have the DATA_SOURCE parameter.
|
||||
* <p>
|
||||
* Subclasses are not discoverable. To create discoverable columns for the framework, you must
|
||||
* extends {@link DynamicTableColumnExtensionPoint}.
|
||||
*
|
||||
* @param <ROW_TYPE> the row type
|
||||
* @param <COLUMN_TYPE> the column type
|
||||
*/
|
||||
public abstract class AbstractDynamicTableColumnStub<ROW_TYPE, COLUMN_TYPE> extends
|
||||
AbstractDynamicTableColumn<ROW_TYPE, COLUMN_TYPE, Object> {
|
||||
public abstract class AbstractDynamicTableColumnStub<ROW_TYPE, COLUMN_TYPE>
|
||||
extends AbstractDynamicTableColumn<ROW_TYPE, COLUMN_TYPE, Object> {
|
||||
|
||||
@Override
|
||||
public COLUMN_TYPE getValue(ROW_TYPE rowObject, Settings settings, Object data,
|
||||
|
|
|
@ -156,7 +156,7 @@ public class AnyObjectTableModel<T> extends GDynamicColumnTableModel<T, Object>
|
|||
}
|
||||
|
||||
private class MethodColumn extends AbstractDynamicTableColumn<T, Object, Object> {
|
||||
private String name;
|
||||
private String methodName;
|
||||
private Method method;
|
||||
private Class<?> returnType;
|
||||
|
||||
|
@ -166,7 +166,7 @@ public class AnyObjectTableModel<T> extends GDynamicColumnTableModel<T, Object>
|
|||
init(m);
|
||||
}
|
||||
catch (NoSuchMethodException | SecurityException e) {
|
||||
name = "No method: " + methodName;
|
||||
this.methodName = "No method: " + methodName;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,12 +181,12 @@ public class AnyObjectTableModel<T> extends GDynamicColumnTableModel<T, Object>
|
|||
|
||||
private void init(Method m) {
|
||||
this.method = m;
|
||||
name = method.getName();
|
||||
if (name.startsWith("get")) {
|
||||
name = name.substring(3);
|
||||
methodName = method.getName();
|
||||
if (methodName.startsWith("get")) {
|
||||
methodName = methodName.substring(3);
|
||||
}
|
||||
|
||||
name = fromCamelCase(name);
|
||||
methodName = fromCamelCase(methodName);
|
||||
returnType = method.getReturnType();
|
||||
}
|
||||
|
||||
|
@ -198,15 +198,15 @@ public class AnyObjectTableModel<T> extends GDynamicColumnTableModel<T, Object>
|
|||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return name;
|
||||
return methodName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(T rowObject, Settings settings, Object dataSource,
|
||||
ServiceProvider sp) throws IllegalArgumentException {
|
||||
if (method == null) {
|
||||
Msg.error(this,
|
||||
"No method '" + name + "' on class" + rowObject.getClass().getSimpleName());
|
||||
Msg.error(this, "No method '" + methodName + "' on class " +
|
||||
rowObject.getClass().getSimpleName());
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
|
|
|
@ -76,7 +76,6 @@ public class GFilterTable<ROW_OBJECT> extends JPanel {
|
|||
}
|
||||
|
||||
private void buildThreadedTable() {
|
||||
@SuppressWarnings("unchecked")
|
||||
GThreadedTablePanel<ROW_OBJECT> tablePanel =
|
||||
createThreadedTablePanel((ThreadedTableModel<ROW_OBJECT, ?>) model);
|
||||
table = tablePanel.getTable();
|
||||
|
|
|
@ -168,6 +168,20 @@ public class GTableWidget<T> extends JPanel {
|
|||
return table.getSelectedRowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selection mode of this table.
|
||||
*
|
||||
* @param mode the mode
|
||||
* @see ListSelectionModel#setSelectionMode(int)
|
||||
*/
|
||||
public void setSelectionMode(int mode) {
|
||||
table.getSelectionModel().setSelectionMode(mode);
|
||||
}
|
||||
|
||||
public int getSelectionMode() {
|
||||
return table.getSelectionModel().getSelectionMode();
|
||||
}
|
||||
|
||||
public void addSelectionListener(ObjectSelectedListener<T> l) {
|
||||
gFilterTable.addSelectionListener(l);
|
||||
}
|
||||
|
|
|
@ -17,12 +17,13 @@ package docking.widgets.table;
|
|||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class TableColumnDescriptor<ROW_TYPE> {
|
||||
private List<TableColumnInfo> columns = new ArrayList<>();
|
||||
|
||||
public List<DynamicTableColumn<ROW_TYPE, ?, ?>> getAllColumns() {
|
||||
List<DynamicTableColumn<ROW_TYPE, ?, ?>> list =
|
||||
new ArrayList<>();
|
||||
List<DynamicTableColumn<ROW_TYPE, ?, ?>> list = new ArrayList<>();
|
||||
for (TableColumnInfo info : columns) {
|
||||
list.add(info.column);
|
||||
}
|
||||
|
@ -30,8 +31,7 @@ public class TableColumnDescriptor<ROW_TYPE> {
|
|||
}
|
||||
|
||||
public List<DynamicTableColumn<ROW_TYPE, ?, ?>> getDefaultVisibleColumns() {
|
||||
List<DynamicTableColumn<ROW_TYPE, ?, ?>> list =
|
||||
new ArrayList<>();
|
||||
List<DynamicTableColumn<ROW_TYPE, ?, ?>> list = new ArrayList<>();
|
||||
for (TableColumnInfo info : columns) {
|
||||
if (info.isVisible) {
|
||||
list.add(info.column);
|
||||
|
@ -60,20 +60,31 @@ public class TableColumnDescriptor<ROW_TYPE> {
|
|||
return editor.createTableSortState();
|
||||
}
|
||||
|
||||
private int remove(DynamicTableColumn<ROW_TYPE, ?, ?> column) {
|
||||
for (int i = 0; i < columns.size(); i++) {
|
||||
TableColumnDescriptor<ROW_TYPE>.TableColumnInfo info = columns.get(i);
|
||||
if (info.column == column) {
|
||||
columns.remove(i);
|
||||
return i;
|
||||
}
|
||||
public void setVisible(String columnName, boolean visible) {
|
||||
TableColumnInfo info = getColumn(columnName);
|
||||
if (info == null) {
|
||||
Msg.debug(this,
|
||||
"Unable to change visibility state of column '%s'".formatted(columnName));
|
||||
return;
|
||||
}
|
||||
if (visible) {
|
||||
info.isVisible = true;
|
||||
}
|
||||
else {
|
||||
// remove and add a new info to clear any sort state info for a hidden column
|
||||
int index = columns.indexOf(info);
|
||||
columns.set(index, new TableColumnInfo(info.column));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void setHidden(DynamicTableColumn<ROW_TYPE, ?, ?> column) {
|
||||
int index = remove(column);
|
||||
columns.add(index, new TableColumnInfo(column));
|
||||
private TableColumnInfo getColumn(String name) {
|
||||
for (TableColumnInfo info : columns) {
|
||||
String columnName = info.column.getColumnName();
|
||||
if (columnName.equals(name)) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addHiddenColumn(DynamicTableColumn<ROW_TYPE, ?, ?> column) {
|
||||
|
@ -105,8 +116,8 @@ public class TableColumnDescriptor<ROW_TYPE> {
|
|||
this.column = column;
|
||||
}
|
||||
|
||||
TableColumnInfo(DynamicTableColumn<ROW_TYPE, ?, ?> column, boolean isVisible,
|
||||
int sortIndex, boolean ascending) {
|
||||
TableColumnInfo(DynamicTableColumn<ROW_TYPE, ?, ?> column, boolean isVisible, int sortIndex,
|
||||
boolean ascending) {
|
||||
this.column = column;
|
||||
this.isVisible = isVisible;
|
||||
this.sortIndex = sortIndex;
|
||||
|
|
|
@ -35,9 +35,11 @@ public class IncrementalLoadJob<ROW_OBJECT> extends Job implements ThreadedTable
|
|||
|
||||
/**
|
||||
* Used to signal that the updateManager has finished loading the final contents gathered
|
||||
* by this job.
|
||||
* by this job. By default, the value is 0, which means there is nothing to wait for. If we
|
||||
* flush, this will be set to 1.
|
||||
*/
|
||||
private final CountDownLatch completedCallbackLatch = new CountDownLatch(1);
|
||||
private CountDownLatch completedCallbackLatch = new CountDownLatch(0);
|
||||
private volatile boolean isCancelled = false;
|
||||
|
||||
private volatile IncrementalUpdatingAccumulator incrementalAccumulator;
|
||||
|
||||
|
@ -74,45 +76,49 @@ public class IncrementalLoadJob<ROW_OBJECT> extends Job implements ThreadedTable
|
|||
}
|
||||
|
||||
boolean interrupted = Thread.currentThread().isInterrupted();
|
||||
notifyCompleted(monitor.isCancelled() || interrupted);
|
||||
notifyCompleted(hasBeenCancelled(monitor) || interrupted);
|
||||
|
||||
// all data should have been posted at this point; clean up any data left in the accumulator
|
||||
incrementalAccumulator.clear();
|
||||
}
|
||||
|
||||
private void doExecute(TaskMonitor monitor) {
|
||||
try {
|
||||
threadedModel.doLoad(incrementalAccumulator, monitor);
|
||||
if (!monitor.isCancelled()) { // in case the model didn't call checkCancelled()
|
||||
flush(incrementalAccumulator);
|
||||
}
|
||||
flush(incrementalAccumulator, monitor);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// handled by the caller of this method
|
||||
isCancelled = true;
|
||||
}
|
||||
|
||||
if (monitor.isCancelled()) {
|
||||
return; // must leave now or we will block in the call below
|
||||
}
|
||||
|
||||
waitForThreadedTableUpdateManager();
|
||||
}
|
||||
|
||||
private void waitForThreadedTableUpdateManager() {
|
||||
try {
|
||||
completedCallbackLatch.await();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
// This implies the user has cancelled the job by starting a new one or that we have
|
||||
// been disposed. Whatever the cause, we want to let the control flow continue as
|
||||
// normal.
|
||||
Thread.currentThread().interrupt(); // preserve the interrupt status
|
||||
}
|
||||
/**
|
||||
* This method tracks cancelled from the given monitor and from any cancelled exceptions that
|
||||
* happen during loading. When loading, the client may trigger a cancelled exception even
|
||||
* though the monitor has not been cancelled.
|
||||
* @param monitor the task monitor
|
||||
* @return true if cancelled
|
||||
*/
|
||||
private boolean hasBeenCancelled(TaskMonitor monitor) {
|
||||
return isCancelled || monitor.isCancelled();
|
||||
}
|
||||
|
||||
private void flush(IncrementalUpdatingAccumulator accumulator) {
|
||||
private void flush(IncrementalUpdatingAccumulator accumulator, TaskMonitor monitor) {
|
||||
|
||||
//
|
||||
// Acquire the update manager lock so that it doesn't send out any events while we are
|
||||
// giving it the data we just finished loading.
|
||||
//
|
||||
synchronized (updateManager.getSynchronizingLock()) {
|
||||
|
||||
if (hasBeenCancelled(monitor)) {
|
||||
// Check for cancelled inside of this lock. This guarantees that no events will be
|
||||
// sent out before we can add our listener
|
||||
return;
|
||||
}
|
||||
|
||||
// push the data to the update manager...
|
||||
accumulator.flushData();
|
||||
|
||||
|
@ -135,8 +141,23 @@ public class IncrementalLoadJob<ROW_OBJECT> extends Job implements ThreadedTable
|
|||
// -A block on jobDone() can now complete as we release the lock
|
||||
// -jobDone() will notify listeners in an invokeLater(), which puts it behind ours
|
||||
//
|
||||
completedCallbackLatch = new CountDownLatch(1);
|
||||
Swing.runLater(() -> updateManager.addThreadedTableListener(IncrementalLoadJob.this));
|
||||
}
|
||||
|
||||
waitForThreadedTableUpdateManagerToFinish();
|
||||
}
|
||||
|
||||
private void waitForThreadedTableUpdateManagerToFinish() {
|
||||
try {
|
||||
completedCallbackLatch.await();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
// This implies the user has cancelled the job by starting a new one or that we have
|
||||
// been disposed. Whatever the cause, we want to let the control flow continue as
|
||||
// normal.
|
||||
Thread.currentThread().interrupt(); // preserve the interrupt status
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyStarted(TaskMonitor monitor) {
|
||||
|
@ -151,14 +172,19 @@ public class IncrementalLoadJob<ROW_OBJECT> extends Job implements ThreadedTable
|
|||
}
|
||||
|
||||
updateManager.removeThreadedTableListener(this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
updateManager.getTaskMonitor().cancel();
|
||||
// monitor.cancel(); TODO: are we handling this ??
|
||||
super.cancel();
|
||||
isCancelled = true;
|
||||
incrementalAccumulator.cancel();
|
||||
|
||||
// Note: cannot do this here, since the cancel() call may happen asynchronously and after
|
||||
// a call to reload() on the table model. Assume that the model itself has already
|
||||
// cancelled the update manager when the worker queue was cancelled. See
|
||||
// ThreadedTableModel.reload().
|
||||
// updateManager.cancelAllJobs();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -188,14 +214,15 @@ public class IncrementalLoadJob<ROW_OBJECT> extends Job implements ThreadedTable
|
|||
* is being provided to the accumulator.
|
||||
*/
|
||||
private class IncrementalUpdatingAccumulator extends SynchronizedListAccumulator<ROW_OBJECT> {
|
||||
private volatile boolean cancelledOrDone;
|
||||
private volatile boolean isDone;
|
||||
private Runnable runnable = () -> {
|
||||
|
||||
if (cancelledOrDone) {
|
||||
if (isCancelledOrDone()) {
|
||||
// this handles the case where a cancel request came in off the Swing
|
||||
// thread whilst we were already posted
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
updateManager.reloadSpecificData(asList());
|
||||
}
|
||||
|
@ -203,7 +230,7 @@ public class IncrementalLoadJob<ROW_OBJECT> extends Job implements ThreadedTable
|
|||
|
||||
// note: check for cancelled again, as it may have been called after the initial
|
||||
// check above if the cancel call was requested off the Swing thread.
|
||||
if (!cancelledOrDone) {
|
||||
if (!isCancelledOrDone()) {
|
||||
Msg.error(this, "Exception incrementally loading table data", e);
|
||||
}
|
||||
}
|
||||
|
@ -219,8 +246,11 @@ public class IncrementalLoadJob<ROW_OBJECT> extends Job implements ThreadedTable
|
|||
swingUpdateManager.update();
|
||||
}
|
||||
|
||||
private boolean isCancelledOrDone() {
|
||||
return isCancelled || isDone;
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
cancelledOrDone = true;
|
||||
swingUpdateManager.dispose();
|
||||
}
|
||||
|
||||
|
@ -233,7 +263,7 @@ public class IncrementalLoadJob<ROW_OBJECT> extends Job implements ThreadedTable
|
|||
}
|
||||
|
||||
void flushData() {
|
||||
cancelledOrDone = true;
|
||||
isDone = true;
|
||||
swingUpdateManager.dispose();
|
||||
updateManager.reloadSpecificData(asList());
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
|
|
@ -145,6 +145,10 @@ public abstract class ThreadedTableModel<ROW_OBJECT, DATA_SOURCE>
|
|||
updateManager.addThreadedTableListener(new NonIncrementalUpdateManagerListener());
|
||||
}
|
||||
|
||||
startInitialLoad();
|
||||
}
|
||||
|
||||
protected void startInitialLoad() {
|
||||
// We are expecting to be in the swing thread. We want the reload to happen after our
|
||||
// constructor is fully completed since the reload will cause our initialize method to
|
||||
// be called in another thread, thereby creating a possible race condition.
|
||||
|
|
|
@ -36,27 +36,30 @@ public class TestDataKeyModel extends ThreadedTableModelStub<Long> {
|
|||
public final static int STRING_COL = 6;
|
||||
|
||||
private Byte[] bytes = new Byte[] { Byte.valueOf((byte) 0x09), Byte.valueOf((byte) 0x03),
|
||||
Byte.valueOf((byte) 0x0c), Byte.valueOf((byte) 0x55), Byte.valueOf((byte) 0x00), Byte.valueOf((byte) 0xdf),
|
||||
Byte.valueOf((byte) 0xff), Byte.valueOf((byte) 0x03), Byte.valueOf((byte) 0x16), Byte.valueOf((byte) 0x02),
|
||||
Byte.valueOf((byte) 0x03), Byte.valueOf((byte) 0x04), };
|
||||
Byte.valueOf((byte) 0x0c), Byte.valueOf((byte) 0x55), Byte.valueOf((byte) 0x00),
|
||||
Byte.valueOf((byte) 0xdf), Byte.valueOf((byte) 0xff), Byte.valueOf((byte) 0x03),
|
||||
Byte.valueOf((byte) 0x16), Byte.valueOf((byte) 0x02), Byte.valueOf((byte) 0x03),
|
||||
Byte.valueOf((byte) 0x04), };
|
||||
|
||||
private Short[] shorts = new Short[] { Short.valueOf((short) 0x0841), Short.valueOf((short) 0xb0f7),
|
||||
Short.valueOf((short) 0xf130), Short.valueOf((short) 0x84e3), Short.valueOf((short) 0x2976),
|
||||
Short.valueOf((short) 0x17d9), Short.valueOf((short) 0xf146), Short.valueOf((short) 0xc4a5),
|
||||
Short.valueOf((short) 0x88f1), Short.valueOf((short) 0x966d), Short.valueOf((short) 0x966e),
|
||||
Short.valueOf((short) 0x966f), };
|
||||
private Short[] shorts = new Short[] { Short.valueOf((short) 0x0841),
|
||||
Short.valueOf((short) 0xb0f7), Short.valueOf((short) 0xf130), Short.valueOf((short) 0x84e3),
|
||||
Short.valueOf((short) 0x2976), Short.valueOf((short) 0x17d9), Short.valueOf((short) 0xf146),
|
||||
Short.valueOf((short) 0xc4a5), Short.valueOf((short) 0x88f1), Short.valueOf((short) 0x966d),
|
||||
Short.valueOf((short) 0x966e), Short.valueOf((short) 0x966f), };
|
||||
|
||||
private Integer[] ints =
|
||||
new Integer[] { Integer.valueOf(0x039D492B), Integer.valueOf(0x0A161497), Integer.valueOf(0x06AA1497),
|
||||
Integer.valueOf(0x0229EE9E), Integer.valueOf(0xFB7428E1), Integer.valueOf(0xD2B4ED2F),
|
||||
Integer.valueOf(0x0C1F67DE), Integer.valueOf(0x0E61C987), Integer.valueOf(0x0133751F),
|
||||
Integer.valueOf(0x07B39541), Integer.valueOf(0x07B39542), Integer.valueOf(0x07B39542), };
|
||||
private Integer[] ints = new Integer[] { Integer.valueOf(0x039D492B),
|
||||
Integer.valueOf(0x0A161497), Integer.valueOf(0x06AA1497), Integer.valueOf(0x0229EE9E),
|
||||
Integer.valueOf(0xFB7428E1), Integer.valueOf(0xD2B4ED2F), Integer.valueOf(0x0C1F67DE),
|
||||
Integer.valueOf(0x0E61C987), Integer.valueOf(0x0133751F), Integer.valueOf(0x07B39541),
|
||||
Integer.valueOf(0x07B39542), Integer.valueOf(0x07B39542), };
|
||||
|
||||
private Long[] longs = new Long[] { Long.valueOf(0x0000000DFAA00C4FL),
|
||||
Long.valueOf(0x00000001FD7CA6A6L), Long.valueOf(0xFFFFFFF4D0EB4AB8L), Long.valueOf(0x0000000445246143L),
|
||||
Long.valueOf(0xFFFFFFF5696F1780L), Long.valueOf(0x0000000685526E5DL), Long.valueOf(0x00000009A1FD98EEL),
|
||||
Long.valueOf(0x00000004AD2B1869L), Long.valueOf(0x00000002928E64C8L), Long.valueOf(0x000000071CE1DDB2L),
|
||||
Long.valueOf(0x000000071CE1DDB3L), Long.valueOf(0x000000071CE1DDB4L), };
|
||||
private Long[] longs =
|
||||
new Long[] { Long.valueOf(0x0000000DFAA00C4FL), Long.valueOf(0x00000001FD7CA6A6L),
|
||||
Long.valueOf(0xFFFFFFF4D0EB4AB8L), Long.valueOf(0x0000000445246143L),
|
||||
Long.valueOf(0xFFFFFFF5696F1780L), Long.valueOf(0x0000000685526E5DL),
|
||||
Long.valueOf(0x00000009A1FD98EEL), Long.valueOf(0x00000004AD2B1869L),
|
||||
Long.valueOf(0x00000002928E64C8L), Long.valueOf(0x000000071CE1DDB2L),
|
||||
Long.valueOf(0x000000071CE1DDB3L), Long.valueOf(0x000000071CE1DDB4L), };
|
||||
|
||||
private Float[] floats =
|
||||
new Float[] { Float.valueOf((float) 0.143111240), Float.valueOf((float) 0.084097680),
|
||||
|
@ -77,7 +80,7 @@ public class TestDataKeyModel extends ThreadedTableModelStub<Long> {
|
|||
protected String[] strings = new String[] { "one", "two", "THREE", "Four", "FiVe", "sIx",
|
||||
"SeVEn", "EighT", "NINE", "ten", "ten", "ten" };
|
||||
|
||||
private long timeBetweenAddingDataItemsInMillis = 1;
|
||||
private volatile long timeBetweenAddingDataItemsInMillis = 1;
|
||||
|
||||
private volatile IncrementalLoadJob<Long> loadJob = null;
|
||||
|
||||
|
|
|
@ -48,4 +48,9 @@ public class ConcurrentListenerSet<T> implements Iterable<T> {
|
|||
public List<T> asList() {
|
||||
return new ArrayList<>(storage.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return asList().toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,14 +41,14 @@ import ghidra.util.task.TaskMonitor;
|
|||
* // do work here...
|
||||
* }
|
||||
* };
|
||||
*
|
||||
*
|
||||
* ConcurrentQBuilder<ITEM, RESULT> builder = new ConcurrentQBuilder<ITEM, RESULT>();
|
||||
* builder.setThreadPoolName("Thread Pool Name");
|
||||
* concurrentQ = builder.getQueue(callback);
|
||||
* ...
|
||||
* ...
|
||||
* concurrentQ.add(item); // where item is one of the instances of ITEM
|
||||
*
|
||||
*
|
||||
* }</pre>
|
||||
* <hr>
|
||||
* <p>
|
||||
|
@ -59,14 +59,14 @@ import ghidra.util.task.TaskMonitor;
|
|||
* // do work here...
|
||||
* }
|
||||
* };
|
||||
*
|
||||
*
|
||||
* {@literal QItemListener<ITEM, RESULT> itemListener = new QItemListener<ITEM, RESULT>()} {
|
||||
* {@literal public void itemProcessed(QResult<ITEM, RESULT> result)} {
|
||||
* RESULT result = result.getResult();
|
||||
* <span style="color:blue"><b>// work on my result...</b></span>
|
||||
* }
|
||||
* };
|
||||
*
|
||||
*
|
||||
* {@literal ConcurrentQBuilder<ITEM, RESULT> builder = new ConcurrentQBuilder<ITEM, RESULT>()};
|
||||
* builder.setThreadPoolName("Thread Pool Name");
|
||||
* <span style="color:blue"><b>builder.setListener(itemListener);</b></span>
|
||||
|
@ -76,9 +76,9 @@ import ghidra.util.task.TaskMonitor;
|
|||
* concurrentQ.add(item); // where item is one of the instances of ITEM
|
||||
* concurrentQ.add(item);
|
||||
* concurrentQ.add(item);
|
||||
*
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* <hr>
|
||||
* <p>
|
||||
* <u>Put Items and Handle Results When All Items Have Been Processed:</u>
|
||||
|
@ -99,10 +99,10 @@ import ghidra.util.task.TaskMonitor;
|
|||
* concurrentQ.add(item);
|
||||
* concurrentQ.add(item);
|
||||
* ...
|
||||
*
|
||||
*
|
||||
* <span style="color:blue"><b>{@literal List<QResult<I, R>> results = concurrentQ.waitForResults();}</b></span>{@literal
|
||||
* // process the results...
|
||||
*
|
||||
*
|
||||
* }</pre>
|
||||
* <hr>
|
||||
* <p>
|
||||
|
@ -120,7 +120,7 @@ import ghidra.util.task.TaskMonitor;
|
|||
* // work on my result...
|
||||
* }
|
||||
* };
|
||||
*
|
||||
*
|
||||
* {@literal ConcurrentQBuilder<ITEM, RESULT> builder = new ConcurrentQBuilder<ITEM, RESULT>()};
|
||||
* builder.setThreadPoolName("Thread Pool Name");
|
||||
* <span style="color:blue"><b>builder.setQueue(new LinkedBlockingQueue(100));</b></span>
|
||||
|
@ -129,10 +129,10 @@ import ghidra.util.task.TaskMonitor;
|
|||
* ...
|
||||
* {@literal Iterator<ITEM> iterator = <get an iterator for 1000s of items somewhere>}
|
||||
* <span style="color:blue"><b>{@code concurrentQ.offer(iterator); // this call will block when the queue fills up (100 items or more)}</b></span>
|
||||
*
|
||||
*
|
||||
* </pre>
|
||||
* <hr>
|
||||
*
|
||||
*
|
||||
* @param <I> The type of the items to be processed.
|
||||
* @param <R> The type of objects resulting from processing an item; if you don't care about the
|
||||
* return value, then make this value whatever you want, like <code>Object</code> or the
|
||||
|
@ -162,7 +162,7 @@ public class ConcurrentQ<I, R> {
|
|||
/**
|
||||
* Creates a ConcurrentQ that will process as many items as the given threadPool can handle
|
||||
* at one time.
|
||||
*
|
||||
*
|
||||
* @param name The name of the thread pool that will be created by this constructor.
|
||||
* @param callback the QWorker object that will be used to process items concurrently.
|
||||
*/
|
||||
|
@ -174,7 +174,7 @@ public class ConcurrentQ<I, R> {
|
|||
/**
|
||||
* Creates a ConcurrentQ that will process at most maxInProgress items at a time, regardless of
|
||||
* how many threads are available in the GThreadPool.
|
||||
*
|
||||
*
|
||||
* @param callback the QWorker object that will be used to process items concurrently.
|
||||
* @param queue the internal storage queue to use in this concurrent queue.
|
||||
* @param threadPool the GThreadPool to used for providing the threads for concurrent processing.
|
||||
|
@ -210,7 +210,7 @@ public class ConcurrentQ<I, R> {
|
|||
/**
|
||||
* Adds a progress listener for this queue. All the progress and messages reported by a
|
||||
* QWorker will be routed to these listener.
|
||||
*
|
||||
*
|
||||
* @param listener the listener for receiving progress and message notifications.
|
||||
*/
|
||||
public synchronized void addProgressListener(QProgressListener<I> listener) {
|
||||
|
@ -239,7 +239,7 @@ public class ConcurrentQ<I, R> {
|
|||
|
||||
/**
|
||||
* Sets the monitor to use with this queue.
|
||||
*
|
||||
*
|
||||
* @param monitor the monitor to attache to this queue
|
||||
* @param cancelClearsAllItems if true, cancelling the monitor will cancel all items currently
|
||||
* being processed by a thread and clear the scheduled
|
||||
|
@ -299,7 +299,7 @@ public class ConcurrentQ<I, R> {
|
|||
* <p>
|
||||
* To enable blocking on the queue when it is full, construct this <code>ConcurrentQ</code>
|
||||
* with an instance of {@link BlockingQueue}.
|
||||
*
|
||||
*
|
||||
* @param iterator An iterator from which items will be taken.
|
||||
* @throws InterruptedException if this queue is interrupted while waiting to add more items
|
||||
*/
|
||||
|
@ -355,7 +355,7 @@ public class ConcurrentQ<I, R> {
|
|||
* <P>
|
||||
* You can still call this method to wait for items to be processed, even if you did not
|
||||
* specify to collect results. In that case, the list returned will be empty.
|
||||
*
|
||||
*
|
||||
* @return the list of QResult objects that have all the results of the completed jobs.
|
||||
* @throws InterruptedException if this call was interrupted--Note: this interruption only
|
||||
* happens if the calling thread cannot acquire the lock. If the thread is
|
||||
|
@ -376,7 +376,7 @@ public class ConcurrentQ<I, R> {
|
|||
|
||||
/**
|
||||
* Wait until at least one result is available and then return the first result.
|
||||
*
|
||||
*
|
||||
* @return the first available result
|
||||
* @throws InterruptedException if interrupted while waiting for a result
|
||||
* @throws IllegalStateException if this queue has been set to not collect results
|
||||
|
@ -416,7 +416,7 @@ public class ConcurrentQ<I, R> {
|
|||
* all results, both with and without exceptions, which you can then process, including
|
||||
* checking for exceptions. Note that to use {@link #waitForResults()} to examine exceptions,
|
||||
* you must have created this queue with <code>collectResults</code> as true.
|
||||
*
|
||||
*
|
||||
* @throws InterruptedException if interrupted while waiting for a result
|
||||
* @throws Exception any exception encountered while processing an item (this will cancel all
|
||||
* items in the queue).
|
||||
|
@ -451,7 +451,7 @@ public class ConcurrentQ<I, R> {
|
|||
* <P>
|
||||
* You can still call this method to wait for items to be processed, even if you did not
|
||||
* specify to collect results. In that case, the list returned will be empty.
|
||||
*
|
||||
*
|
||||
* @param timeout the timeout
|
||||
* @param unit the timeout unit
|
||||
* @return the list of QResult objects that have all the results of the completed jobs.
|
||||
|
@ -481,7 +481,7 @@ public class ConcurrentQ<I, R> {
|
|||
* they check the isCancelled() state of their QMonitor, it will be true. Setting the
|
||||
* interruptRunningTasks to true, will result in a thread interrupt to any currently running
|
||||
* task which might be useful if the task perform waiting operations like I/O.
|
||||
*
|
||||
*
|
||||
* @param interruptRunningTasks if true, an attempt will be made to interrupt any currently
|
||||
* processing thread.
|
||||
* @return a list of all items that have not yet been queued to the threadPool.
|
||||
|
@ -541,6 +541,14 @@ public class ConcurrentQ<I, R> {
|
|||
if (threadPool.isPrivate()) {
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
resultList.clear();
|
||||
}
|
||||
finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean waitUntilDone(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
|
|
|
@ -24,11 +24,11 @@ import ghidra.util.task.TaskMonitor;
|
|||
/**
|
||||
* A helper class to build up the potentially complicated {@link ConcurrentQ}.
|
||||
* <P>
|
||||
* Note: you must supply either a {@link GThreadPool} instance or a thread pool name. Further,
|
||||
* Note: you must supply either a {@link GThreadPool} instance or a thread pool name. Further,
|
||||
* if you supply the name of a thread pool, then a private, non-shared pool will be used. If you
|
||||
* wish to make use of a shared pool, then you need to create that thread pool yourself. See
|
||||
* {@link GThreadPool#getSharedThreadPool(String)}.
|
||||
*
|
||||
*
|
||||
* <P>
|
||||
* Examples:
|
||||
* <p>
|
||||
|
@ -38,25 +38,25 @@ import ghidra.util.task.TaskMonitor;
|
|||
* // do work here...
|
||||
* }
|
||||
* };
|
||||
*
|
||||
*
|
||||
* ConcurrentQBuilder<I, R> builder = new ConcurrentQBuilder<I, R>();
|
||||
* builder.setThreadPoolName("Thread Pool Name");
|
||||
* builder.setQueue(new PriorityBlockingQueue());
|
||||
* concurrentQ = builder.build(callback);
|
||||
*
|
||||
*
|
||||
* // OR, you can chain the builder calls:
|
||||
* ConcurrentQBuilder<I, R> builder = new ConcurrentQBuilder<I, R>();
|
||||
* queue = builder.setThreadPoolName("Thread Pool Name").
|
||||
* setQueue(new PriorityBlockingQueue()).
|
||||
* setMaxInProgress(1).
|
||||
* build(callback);
|
||||
*
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
*
|
||||
* Note: if you wish to take advantage of blocking when adding items to the {@link ConcurrentQ},
|
||||
*
|
||||
* Note: if you wish to take advantage of blocking when adding items to the {@link ConcurrentQ},
|
||||
* see {@link #setQueue(Queue)}.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param <I> The type of the items to be processed.
|
||||
|
@ -74,14 +74,14 @@ public class ConcurrentQBuilder<I, R> {
|
|||
private boolean cancelClearsAllJobs = true;
|
||||
|
||||
/**
|
||||
* Sets the queue to be used by the {@link ConcurrentQ}. If you would like advanced features,
|
||||
* like a queue that blocks when too many items have been placed in it, then use an
|
||||
* Sets the queue to be used by the {@link ConcurrentQ}. If you would like advanced features,
|
||||
* like a queue that blocks when too many items have been placed in it, then use an
|
||||
* advanced queue here, such as a {@link LinkedBlockingQueue}.
|
||||
* <p>
|
||||
* Note: if you wish to take advantage of blocking when adding items to the {@link ConcurrentQ},
|
||||
* then be sure to call the appropriate method, such as
|
||||
* Note: if you wish to take advantage of blocking when adding items to the {@link ConcurrentQ},
|
||||
* then be sure to call the appropriate method, such as
|
||||
* {@link ConcurrentQ#offer(java.util.Iterator)}.
|
||||
*
|
||||
*
|
||||
* @param queue the queue to be used by the {@link ConcurrentQ}
|
||||
* @return this builder
|
||||
*/
|
||||
|
@ -91,14 +91,14 @@ public class ConcurrentQBuilder<I, R> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Specifies the maximum number of items that can be process at a time.
|
||||
* If this is set to 0, then the concurrent queue will attempt to execute as many
|
||||
* items at a time as there are threads in the given threadPool. Setting
|
||||
* Specifies the maximum number of items that can be process at a time.
|
||||
* If this is set to 0, then the concurrent queue will attempt to execute as many
|
||||
* items at a time as there are threads in the given threadPool. Setting
|
||||
* this parameter to 1 will have the effect of guaranteeing that
|
||||
* all times are processed one at a time in the order they were submitted.
|
||||
* Any other positive value will run that many items concurrently,
|
||||
* Any other positive value will run that many items concurrently,
|
||||
* up to the number of available threads.
|
||||
*
|
||||
*
|
||||
* @param max the max number of items to execute at one time; defaults to 0
|
||||
* @return this builder instance
|
||||
*/
|
||||
|
@ -109,9 +109,9 @@ public class ConcurrentQBuilder<I, R> {
|
|||
|
||||
/**
|
||||
* Sets the name to be used when creating a <b>private thread pool</b>. If you wish to use
|
||||
* a <i>shared thread pool</i>, then you need to create that thread pool youself and call
|
||||
* a <i>shared thread pool</i>, then you need to create that thread pool yourself and call
|
||||
* {@link #setThreadPool(GThreadPool)}.
|
||||
*
|
||||
*
|
||||
* @param name the name of the thread pool.
|
||||
* @return this builder instance
|
||||
* @see GThreadPool#getSharedThreadPool(String)
|
||||
|
@ -123,9 +123,9 @@ public class ConcurrentQBuilder<I, R> {
|
|||
|
||||
/**
|
||||
* Use the given thread pool for processing the work items. If you do not care to configure
|
||||
* the thread pool used and you do not wish to make use of shared thread pools, then you
|
||||
* the thread pool used and you do not wish to make use of shared thread pools, then you
|
||||
* can call {@link #setThreadPoolName(String)} instead of this method.
|
||||
*
|
||||
*
|
||||
* @param threadPool the thread pool to use
|
||||
* @return this builder instance
|
||||
* @see GThreadPool#getSharedThreadPool(String)
|
||||
|
@ -137,7 +137,8 @@ public class ConcurrentQBuilder<I, R> {
|
|||
|
||||
/**
|
||||
* Specifies if the concurrent queue should collect the results as items are processed
|
||||
* so they can be returned in a {@link ConcurrentQ#waitForResults()} call.
|
||||
* so they can be returned in a {@link ConcurrentQ#waitForResults()} or
|
||||
* {@link ConcurrentQ#waitForNextResult()} call.
|
||||
* @param collectResults true signals to collect the generated results; defaults to false
|
||||
* @return this builder instance
|
||||
*/
|
||||
|
@ -147,18 +148,18 @@ public class ConcurrentQBuilder<I, R> {
|
|||
}
|
||||
|
||||
/**
|
||||
* True signals that the jobs run by the client wish to report progress. The default value
|
||||
* True signals that the jobs run by the client wish to report progress. The default value
|
||||
* is false.
|
||||
* <p>
|
||||
* The default of false is good for clients that have a known amount of work to be processed.
|
||||
* In this case, a total count of work jobs is maintained by the queue. As items are
|
||||
* In this case, a total count of work jobs is maintained by the queue. As items are
|
||||
* completed, the queue will update the monitor provided to it at construction time to reflect
|
||||
* the number of jobs completed as work is done. On the other hand, some clients have
|
||||
* known known number of jobs to complete, but simply add work to the queue as it arrives.
|
||||
* In that case, the client should update its monitor for progress, as the queue cannot
|
||||
* the number of jobs completed as work is done. On the other hand, some clients have
|
||||
* known known number of jobs to complete, but simply add work to the queue as it arrives.
|
||||
* In that case, the client should update its monitor for progress, as the queue cannot
|
||||
* do so in a meaningful way.
|
||||
*
|
||||
* @param reportsProgress true signals that the client will update progress; false signals
|
||||
*
|
||||
* @param reportsProgress true signals that the client will update progress; false signals
|
||||
* that the queue should do so
|
||||
* @return this builder instance
|
||||
*/
|
||||
|
@ -178,12 +179,12 @@ public class ConcurrentQBuilder<I, R> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets whether a cancel will clear all jobs (current and pending) or just the
|
||||
* Sets whether a cancel will clear all jobs (current and pending) or just the
|
||||
* current jobs being processed. The default value is {@code true}.
|
||||
*
|
||||
* @param clearAllJobs if true, cancelling the monitor will cancel all items currently being
|
||||
* processed by a thread and clear the scheduled items that haven't yet run. If false,
|
||||
* only the items currently being processed will be cancelled.
|
||||
*
|
||||
* @param clearAllJobs if true, cancelling the monitor will cancel all items currently being
|
||||
* processed by a thread and clear the scheduled items that haven't yet run. If false,
|
||||
* only the items currently being processed will be cancelled.
|
||||
* @return this builder
|
||||
* @see ConcurrentQ#setMonitor(TaskMonitor, boolean)
|
||||
*/
|
||||
|
@ -194,9 +195,8 @@ public class ConcurrentQBuilder<I, R> {
|
|||
|
||||
public ConcurrentQ<I, R> build(QCallback<I, R> callback) {
|
||||
|
||||
ConcurrentQ<I, R> concurrentQ =
|
||||
new ConcurrentQ<>(callback, getQueue(), getThreadPool(), listener, collectResults,
|
||||
maxInProgress, jobsReportProgress);
|
||||
ConcurrentQ<I, R> concurrentQ = new ConcurrentQ<>(callback, getQueue(), getThreadPool(),
|
||||
listener, collectResults, maxInProgress, jobsReportProgress);
|
||||
|
||||
if (monitor != null) {
|
||||
concurrentQ.setMonitor(monitor, cancelClearsAllJobs);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -59,6 +58,10 @@ public class SynchronizedListAccumulator<T> implements Accumulator<T> {
|
|||
return list.size();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
list.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Iterator<T> iterator() {
|
||||
return asList().iterator();
|
||||
|
|
|
@ -28,6 +28,8 @@ public abstract class Job {
|
|||
/**
|
||||
* The method that gets called by the Worker when this job is selected to be run
|
||||
* by the Worker.
|
||||
* @param monitor the monitor
|
||||
* @throws CancelledException jobs may choose to throw a cancelled exception
|
||||
*/
|
||||
public abstract void run(TaskMonitor monitor) throws CancelledException;
|
||||
|
||||
|
|
|
@ -155,6 +155,7 @@ public class GThemeDefaults {
|
|||
public static final GColor PURPLE = getColor("purple");
|
||||
public static final GColor RED = getColor("red");
|
||||
public static final GColor SILVER = getColor("silver");
|
||||
public static final GColor TEAL = getColor("teal");
|
||||
public static final GColor WHITE = getColor("white");
|
||||
public static final GColor YELLOW = getColor("yellow");
|
||||
|
||||
|
|
|
@ -633,7 +633,7 @@ public abstract class PluginTool extends AbstractDockingTool {
|
|||
}
|
||||
|
||||
winMgr.restoreWindowDataFromXml(root);
|
||||
winMgr.setToolName(fullName);
|
||||
updateTitle();
|
||||
return hasErrors;
|
||||
}
|
||||
|
||||
|
|
|
@ -225,21 +225,37 @@ public abstract class StandAloneApplication implements GenericStandAloneApplicat
|
|||
}
|
||||
|
||||
private Element getSavedToolElement() {
|
||||
File savedToolFile = new File(Application.getUserSettingsDirectory(), SAVED_TOOL_FILE);
|
||||
if (!savedToolFile.exists()) {
|
||||
return null;
|
||||
File userSettingsDir = Application.getUserSettingsDirectory();
|
||||
File savedToolFile = new File(userSettingsDir, SAVED_TOOL_FILE);
|
||||
if (savedToolFile.exists()) {
|
||||
return loadToolXml(savedToolFile);
|
||||
}
|
||||
|
||||
Msg.debug(this, "No saved tool found in " + userSettingsDir);
|
||||
List<File> dirs = GenericRunInfo.getPreviousApplicationSettingsDirsByTime();
|
||||
for (File dir : dirs) {
|
||||
savedToolFile = new File(dir, SAVED_TOOL_FILE);
|
||||
Msg.debug(this, "Checking for previous tool in " + dir);
|
||||
if (savedToolFile.exists()) {
|
||||
Msg.debug(this, "Using previous tool " + savedToolFile);
|
||||
return loadToolXml(savedToolFile);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Element loadToolXml(File file) {
|
||||
FileInputStream fileInputStream = null;
|
||||
try {
|
||||
fileInputStream = new FileInputStream(savedToolFile.getAbsolutePath());
|
||||
fileInputStream = new FileInputStream(file.getAbsolutePath());
|
||||
SAXBuilder sax = XmlUtilities.createSecureSAXBuilder(false, false);
|
||||
Element root = sax.build(fileInputStream).getRootElement();
|
||||
return root;
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.showError(getClass(), null, "Error Reading Tool",
|
||||
"Could not read tool: " + savedToolFile, e);
|
||||
Msg.showError(getClass(), null, "Error Reading Tool", "Could not read tool: " + file,
|
||||
e);
|
||||
}
|
||||
finally {
|
||||
if (fileInputStream != null) {
|
||||
|
@ -251,7 +267,6 @@ public abstract class StandAloneApplication implements GenericStandAloneApplicat
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue