* Simplyfied BackgroundTransferablePolicy again (no queuing again)
* solves the "add after clear" concurrency problem when adding really lots of files
This commit is contained in:
parent
2026c60b1d
commit
02057b3056
|
@ -10,7 +10,6 @@ import java.util.List;
|
|||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.SwingWorker;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
|
@ -74,25 +73,15 @@ class FileTree extends FileBotTree {
|
|||
public void clear() {
|
||||
((FileTreeTransferablePolicy) getTransferablePolicy()).reset();
|
||||
|
||||
// there may still be some runnables from the transfer in the event queue,
|
||||
// clear the model, after those runnables have finished,
|
||||
// otherwise it may happen, that stuff is added, after the model has been cleared
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
FileTree.super.clear();
|
||||
super.clear();
|
||||
contentChanged();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
void contentChanged() {
|
||||
private void contentChanged() {
|
||||
synchronized (this) {
|
||||
if (postProcessor != null)
|
||||
postProcessor.cancel(false);
|
||||
postProcessor.cancel(true);
|
||||
|
||||
postProcessor = new PostProcessor();
|
||||
postProcessor.execute();
|
||||
|
@ -138,4 +127,5 @@ class FileTree extends FileBotTree {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import java.util.logging.Logger;
|
|||
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.TableModelEvent;
|
||||
import javax.swing.table.TableColumn;
|
||||
import javax.swing.table.TableModel;
|
||||
|
@ -93,17 +92,8 @@ class SfvTable extends JTable implements TransferablePolicySupport, Saveable {
|
|||
public void clear() {
|
||||
((BackgroundFileTransferablePolicy<?>) getTransferablePolicy()).reset();
|
||||
|
||||
// there may still be some runnables from the transfer in the event queue,
|
||||
// clear the model, after those runnables have finished,
|
||||
// otherwise it may happen, that stuff is added, after the model has been cleared
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
((ChecksumTableModel) getModel()).clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public TransferablePolicy getTransferablePolicy() {
|
||||
|
|
|
@ -3,101 +3,64 @@ package net.sourceforge.filebot.ui.transferablepolicies;
|
|||
|
||||
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.SwingWorker;
|
||||
|
||||
import net.sourceforge.tuned.DefaultThreadFactory;
|
||||
import net.sourceforge.tuned.ui.SwingWorkerPropertyChangeAdapter;
|
||||
|
||||
|
||||
public abstract class BackgroundFileTransferablePolicy<V> extends FileTransferablePolicy {
|
||||
|
||||
public static final String LOADING_PROPERTY = "loading";
|
||||
|
||||
private static final ThreadFactory backgroundTransferThreadFactory = new DefaultThreadFactory("BackgroundTransferPool", Thread.NORM_PRIORITY);
|
||||
private BackgroundWorker worker = null;
|
||||
|
||||
private SingleThreadExecutor executor = null;
|
||||
|
||||
private final AtomicInteger count = new AtomicInteger(0);
|
||||
@Override
|
||||
public boolean accept(Transferable tr) {
|
||||
if (isActive())
|
||||
return false;
|
||||
|
||||
return super.accept(tr);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void handleTransferable(Transferable tr, boolean add) {
|
||||
List<File> files = getFilesFromTransferable(tr);
|
||||
|
||||
if (files == null)
|
||||
if ((files == null) || files.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!add)
|
||||
synchronized (this) {
|
||||
reset();
|
||||
|
||||
if (!add) {
|
||||
clear();
|
||||
}
|
||||
|
||||
execute(new LoadFilesTask(files));
|
||||
worker = new BackgroundWorker(files);
|
||||
worker.addPropertyChangeListener(new BackgroundWorkerListener());
|
||||
worker.execute();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected synchronized void execute(Runnable task) {
|
||||
if (executor == null) {
|
||||
executor = new SingleThreadExecutor();
|
||||
}
|
||||
|
||||
executor.execute(task);
|
||||
}
|
||||
|
||||
|
||||
public boolean isActive() {
|
||||
return count.get() > 0;
|
||||
}
|
||||
|
||||
|
||||
private synchronized void setActive(final boolean active) {
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
propertyChangeSupport.firePropertyChange(LOADING_PROPERTY, null, active);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private synchronized void deactivate(boolean shutdownNow) {
|
||||
if (executor != null) {
|
||||
if (shutdownNow) {
|
||||
executor.shutdownNow();
|
||||
} else {
|
||||
executor.shutdown();
|
||||
}
|
||||
|
||||
executor = null;
|
||||
}
|
||||
|
||||
count.set(0);
|
||||
public synchronized boolean isActive() {
|
||||
return (worker != null) && !worker.isDone();
|
||||
}
|
||||
|
||||
|
||||
public synchronized void reset() {
|
||||
deactivate(true);
|
||||
setActive(false);
|
||||
if (isActive()) {
|
||||
worker.cancel(true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends data chunks to the process method.
|
||||
*
|
||||
* @param chunks
|
||||
*/
|
||||
protected final void publish(V... chunks) {
|
||||
SwingUtilities.invokeLater(new ProcessChunksTask(chunks));
|
||||
}
|
||||
|
||||
|
||||
|
@ -110,36 +73,61 @@ public abstract class BackgroundFileTransferablePolicy<V> extends FileTransferab
|
|||
protected abstract void process(List<V> chunks);
|
||||
|
||||
|
||||
private class LoadFilesTask implements Runnable {
|
||||
/**
|
||||
* Sends data chunks to the process method.
|
||||
*
|
||||
* @param chunks
|
||||
*/
|
||||
protected final void publish(V... chunks) {
|
||||
worker.publishChunks(chunks);
|
||||
}
|
||||
|
||||
|
||||
private class BackgroundWorker extends SwingWorker<Object, V> {
|
||||
|
||||
private final List<File> files;
|
||||
|
||||
|
||||
public LoadFilesTask(List<File> files) {
|
||||
public BackgroundWorker(List<File> files) {
|
||||
this.files = files;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
protected Object doInBackground() {
|
||||
load(files);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private class ProcessChunksTask implements Runnable {
|
||||
|
||||
private final V[] chunks;
|
||||
|
||||
|
||||
public ProcessChunksTask(V[] chunks) {
|
||||
this.chunks = chunks;
|
||||
public void publishChunks(V... chunks) {
|
||||
if (!isCancelled()) {
|
||||
publish(chunks);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
process(Arrays.asList(chunks));
|
||||
protected void process(List<V> chunks) {
|
||||
if (!isCancelled()) {
|
||||
BackgroundFileTransferablePolicy.this.process(chunks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class BackgroundWorkerListener extends SwingWorkerPropertyChangeAdapter {
|
||||
|
||||
@Override
|
||||
public void started(PropertyChangeEvent evt) {
|
||||
propertyChangeSupport.firePropertyChange(LOADING_PROPERTY, null, true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void done(PropertyChangeEvent evt) {
|
||||
propertyChangeSupport.firePropertyChange(LOADING_PROPERTY, null, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,37 +142,4 @@ public abstract class BackgroundFileTransferablePolicy<V> extends FileTransferab
|
|||
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
|
||||
propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
|
||||
}
|
||||
|
||||
|
||||
private class SingleThreadExecutor extends ThreadPoolExecutor {
|
||||
|
||||
public SingleThreadExecutor() {
|
||||
super(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), backgroundTransferThreadFactory);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
|
||||
if (count.getAndIncrement() <= 0) {
|
||||
setActive(true);
|
||||
}
|
||||
|
||||
super.execute(command);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void afterExecute(Runnable r, Throwable t) {
|
||||
super.afterExecute(r, t);
|
||||
|
||||
if (count.decrementAndGet() <= 0) {
|
||||
// shutdown executor
|
||||
deactivate(false);
|
||||
setActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ public class DefaultThreadFactory implements ThreadFactory {
|
|||
public DefaultThreadFactory(String groupName, int priority, boolean daemon) {
|
||||
group = new ThreadGroup(groupName);
|
||||
group.setDaemon(daemon);
|
||||
group.setMaxPriority(priority);
|
||||
|
||||
this.daemon = daemon;
|
||||
this.priority = priority;
|
||||
|
@ -30,12 +31,12 @@ public class DefaultThreadFactory implements ThreadFactory {
|
|||
|
||||
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(group, r, String.format("%s-thread-%d", group.getName(), threadNumber.incrementAndGet()));
|
||||
Thread thread = new Thread(group, r, String.format("%s-thread-%d", group.getName(), threadNumber.incrementAndGet()));
|
||||
|
||||
t.setDaemon(daemon);
|
||||
t.setPriority(priority);
|
||||
thread.setDaemon(daemon);
|
||||
thread.setPriority(priority);
|
||||
|
||||
return t;
|
||||
return thread;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue