Merge remote-tracking branch 'origin/GT-3604-dragonmacher-table-upate-delay'

This commit is contained in:
ghidravore 2020-03-20 17:56:10 -04:00
commit 70305cd572

View file

@ -15,14 +15,12 @@
*/ */
package docking.widgets.table.threaded; package docking.widgets.table.threaded;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import javax.swing.SwingUtilities;
import docking.widgets.table.AddRemoveListItem; import docking.widgets.table.AddRemoveListItem;
import docking.widgets.table.TableSortingContext; import docking.widgets.table.TableSortingContext;
import generic.concurrent.ConcurrentListenerSet; import generic.concurrent.ConcurrentListenerSet;
import ghidra.util.Swing;
import ghidra.util.SystemUtilities; import ghidra.util.SystemUtilities;
import ghidra.util.task.*; import ghidra.util.task.*;
@ -35,9 +33,11 @@ import ghidra.util.task.*;
* @param <T> the row type * @param <T> the row type
*/ */
class ThreadedTableModelUpdateMgr<T> { class ThreadedTableModelUpdateMgr<T> {
static final int TOO_MANY_ADD_REMOVES = 3000;
public final static int DELAY = 1000; // make the delay large enough that a flurry of events will stay buffered
public final static int DELAY = 5000;
public final static int MAX_DELAY = 1000 * 60 * 20; public final static int MAX_DELAY = 1000 * 60 * 20;
static final int TOO_MANY_ADD_REMOVES = 3000;
private ThreadedTableModel<T, ?> model; private ThreadedTableModel<T, ?> model;
private SwingUpdateManager addRemoveUpdater; private SwingUpdateManager addRemoveUpdater;
@ -115,49 +115,11 @@ class ThreadedTableModelUpdateMgr<T> {
return addRemoveUpdater; return addRemoveUpdater;
} }
/** private int getMaxAddRemoveCount() {
* Processes the accumulated list of add/remove items. // have the max count grow with the size of the table to prevent too many calls to reload()
* Called when the swing update manager decides its time to run. int n = model.getRowCount();
*/ int cuttoff = (int) (n * .1);
private void processAddRemoveItems() { return Math.max(maxAddRemoveCount, cuttoff);
synchronized (addRemoveUpdater) {
if (addRemoveWaitList.isEmpty()) {
return;
}
// too many requests for add/remove triggers a full reload for efficiency
if (addRemoveWaitList.size() > maxAddRemoveCount) {
reload();
return;
}
pendingJob = new AddRemoveJob<>(model, addRemoveWaitList, monitor);
addRemoveWaitList = new ArrayList<>();
runJob();
}
}
/**
* Called when there is work to be done. It creates a thread if none is running to do the
* work that is built into the pending job.
*/
private void runJob() {
synchronized (addRemoveUpdater) {
if (thread != null) {
return; // if thread exists, it will handle any pending job
}
if (pendingJob == null) {
return; // nothing to do!
}
// Ok, we have to create a thread to process the pending job
thread = new Thread(threadRunnable,
"Threaded Table Model Update Manager: " + model.getName());
thread.start();
SwingUtilities.invokeLater(notifyUpdating);
}
} }
/** /**
@ -184,8 +146,7 @@ class ThreadedTableModelUpdateMgr<T> {
void reload() { void reload() {
synchronized (addRemoveUpdater) { synchronized (addRemoveUpdater) {
cancelAllJobs(); cancelAllJobs();
pendingJob = new LoadJob<>(model, monitor); runJob(new LoadJob<>(model, monitor));
runJob();
} }
} }
@ -193,8 +154,7 @@ class ThreadedTableModelUpdateMgr<T> {
synchronized (addRemoveUpdater) { synchronized (addRemoveUpdater) {
cancelAllJobs(); cancelAllJobs();
TableData<T> tableData = TableData.createFullDataset(data); TableData<T> tableData = TableData.createFullDataset(data);
pendingJob = new LoadSpecificDataJob<>(model, monitor, tableData); runJob(new LoadSpecificDataJob<>(model, monitor, tableData));
runJob();
} }
} }
@ -225,8 +185,7 @@ class ThreadedTableModelUpdateMgr<T> {
return; return;
} }
pendingJob = new SortJob<>(model, monitor, sortingContext, forceSort); runJob(new SortJob<>(model, monitor, sortingContext, forceSort));
runJob();
} }
} }
@ -253,8 +212,7 @@ class ThreadedTableModelUpdateMgr<T> {
return; return;
} }
pendingJob = new FilterJob<>(model, monitor); runJob(new FilterJob<>(model, monitor));
runJob();
} }
} }
@ -270,17 +228,15 @@ class ThreadedTableModelUpdateMgr<T> {
void addRemove(AddRemoveListItem<T> item) { void addRemove(AddRemoveListItem<T> item) {
synchronized (addRemoveUpdater) { synchronized (addRemoveUpdater) {
if (pendingJob != null) { if (pendingJob != null) {
pendingJob.addRemove(item, maxAddRemoveCount); pendingJob.addRemove(item, getMaxAddRemoveCount());
return;
}
if (addRemoveWaitList.size() > maxAddRemoveCount) { // push the updater back to avoid constant reloading
reload(); addRemoveUpdater.updateLater();
return; return;
} }
if (addRemoveWaitList.isEmpty() && thread == null) { if (addRemoveWaitList.isEmpty() && thread == null) {
SwingUtilities.invokeLater(notifyPending); Swing.runLater(notifyPending);
} }
addRemoveWaitList.add(item); addRemoveWaitList.add(item);
@ -288,6 +244,28 @@ class ThreadedTableModelUpdateMgr<T> {
} }
} }
/**
* Processes the accumulated list of add/remove items.
* Called when the swing update manager decides its time to run.
*/
private void processAddRemoveItems() {
synchronized (addRemoveUpdater) {
if (addRemoveWaitList.isEmpty()) {
return;
}
// too many requests for add/remove triggers a full reload for efficiency
if (addRemoveWaitList.size() > getMaxAddRemoveCount()) {
reload();
return;
}
runJob(new AddRemoveJob<>(model, addRemoveWaitList, monitor));
addRemoveWaitList = new ArrayList<>();
}
}
/** /**
* Returns true if there is any scheduled work that has not been completed, including any * Returns true if there is any scheduled work that has not been completed, including any
* deferred add/removes. * deferred add/removes.
@ -356,6 +334,27 @@ class ThreadedTableModelUpdateMgr<T> {
addRemoveUpdater.updateNow(); addRemoveUpdater.updateNow();
} }
/**
* Called when there is work to be done. It creates a thread if none is running to do the
* work that is built into the pending job.
*/
private void runJob(TableUpdateJob<T> job) {
synchronized (addRemoveUpdater) {
// Create a thread to process the pending job
pendingJob = Objects.requireNonNull(job);
if (thread != null) {
return; // if thread exists, it will handle any pending job
}
thread = new Thread(threadRunnable,
"Threaded Table Model Update Manager: " + model.getName());
thread.start();
Swing.runLater(notifyUpdating);
}
}
/** /**
* Transitions the pending job to the current job is a thread safe way. * Transitions the pending job to the current job is a thread safe way.
* @return the new current job. * @return the new current job.
@ -380,22 +379,22 @@ class ThreadedTableModelUpdateMgr<T> {
*/ */
private void jobDone() { private void jobDone() {
// This synchronized is not need, as this method is only called from within a // This synchronized is not needed, as this method is only called from within a
// synchronized block. But, it feels better to be explicit here to signal that // synchronized block. But, it feels better to be explicit here to signal that
// synchronization is needed. // synchronization is needed.
synchronized (addRemoveUpdater) { synchronized (addRemoveUpdater) {
boolean isCancelled = monitor.isCancelled(); boolean isCancelled = monitor.isCancelled();
if (isCancelled) { if (isCancelled) {
SwingUtilities.invokeLater(notifyCancelled); Swing.runLater(notifyCancelled);
} }
else { else {
SwingUtilities.invokeLater(notifyDone); Swing.runLater(notifyDone);
} }
thread = null; thread = null;
if (!addRemoveWaitList.isEmpty()) { if (!addRemoveWaitList.isEmpty()) {
SwingUtilities.invokeLater(notifyPending); Swing.runLater(notifyPending);
} }
} }
} }