diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/threaded/ThreadedTableModelUpdateMgr.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/threaded/ThreadedTableModelUpdateMgr.java index c89c00f8a5..2aba07f810 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/threaded/ThreadedTableModelUpdateMgr.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/threaded/ThreadedTableModelUpdateMgr.java @@ -15,14 +15,12 @@ */ package docking.widgets.table.threaded; -import java.util.ArrayList; -import java.util.List; - -import javax.swing.SwingUtilities; +import java.util.*; import docking.widgets.table.AddRemoveListItem; import docking.widgets.table.TableSortingContext; import generic.concurrent.ConcurrentListenerSet; +import ghidra.util.Swing; import ghidra.util.SystemUtilities; import ghidra.util.task.*; @@ -35,9 +33,11 @@ import ghidra.util.task.*; * @param the row type */ class ThreadedTableModelUpdateMgr { - 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; + static final int TOO_MANY_ADD_REMOVES = 3000; private ThreadedTableModel model; private SwingUpdateManager addRemoveUpdater; @@ -115,49 +115,11 @@ class ThreadedTableModelUpdateMgr { return addRemoveUpdater; } - /** - * 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() > 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); - } + private int getMaxAddRemoveCount() { + // have the max count grow with the size of the table to prevent too many calls to reload() + int n = model.getRowCount(); + int cuttoff = (int) (n * .1); + return Math.max(maxAddRemoveCount, cuttoff); } /** @@ -184,8 +146,7 @@ class ThreadedTableModelUpdateMgr { void reload() { synchronized (addRemoveUpdater) { cancelAllJobs(); - pendingJob = new LoadJob<>(model, monitor); - runJob(); + runJob(new LoadJob<>(model, monitor)); } } @@ -193,8 +154,7 @@ class ThreadedTableModelUpdateMgr { synchronized (addRemoveUpdater) { cancelAllJobs(); TableData tableData = TableData.createFullDataset(data); - pendingJob = new LoadSpecificDataJob<>(model, monitor, tableData); - runJob(); + runJob(new LoadSpecificDataJob<>(model, monitor, tableData)); } } @@ -225,8 +185,7 @@ class ThreadedTableModelUpdateMgr { return; } - pendingJob = new SortJob<>(model, monitor, sortingContext, forceSort); - runJob(); + runJob(new SortJob<>(model, monitor, sortingContext, forceSort)); } } @@ -253,8 +212,7 @@ class ThreadedTableModelUpdateMgr { return; } - pendingJob = new FilterJob<>(model, monitor); - runJob(); + runJob(new FilterJob<>(model, monitor)); } } @@ -270,17 +228,15 @@ class ThreadedTableModelUpdateMgr { void addRemove(AddRemoveListItem item) { synchronized (addRemoveUpdater) { if (pendingJob != null) { - pendingJob.addRemove(item, maxAddRemoveCount); - return; - } + pendingJob.addRemove(item, getMaxAddRemoveCount()); - if (addRemoveWaitList.size() > maxAddRemoveCount) { - reload(); + // push the updater back to avoid constant reloading + addRemoveUpdater.updateLater(); return; } if (addRemoveWaitList.isEmpty() && thread == null) { - SwingUtilities.invokeLater(notifyPending); + Swing.runLater(notifyPending); } addRemoveWaitList.add(item); @@ -288,6 +244,28 @@ class ThreadedTableModelUpdateMgr { } } + /** + * 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 * deferred add/removes. @@ -356,6 +334,27 @@ class ThreadedTableModelUpdateMgr { 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 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. * @return the new current job. @@ -380,22 +379,22 @@ class ThreadedTableModelUpdateMgr { */ 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 // synchronization is needed. synchronized (addRemoveUpdater) { boolean isCancelled = monitor.isCancelled(); if (isCancelled) { - SwingUtilities.invokeLater(notifyCancelled); + Swing.runLater(notifyCancelled); } else { - SwingUtilities.invokeLater(notifyDone); + Swing.runLater(notifyDone); } thread = null; if (!addRemoveWaitList.isEmpty()) { - SwingUtilities.invokeLater(notifyPending); + Swing.runLater(notifyPending); } } }