* 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.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
import javax.swing.SwingWorker;
|
import javax.swing.SwingWorker;
|
||||||
import javax.swing.tree.DefaultMutableTreeNode;
|
import javax.swing.tree.DefaultMutableTreeNode;
|
||||||
import javax.swing.tree.DefaultTreeModel;
|
import javax.swing.tree.DefaultTreeModel;
|
||||||
|
@ -74,25 +73,15 @@ class FileTree extends FileBotTree {
|
||||||
public void clear() {
|
public void clear() {
|
||||||
((FileTreeTransferablePolicy) getTransferablePolicy()).reset();
|
((FileTreeTransferablePolicy) getTransferablePolicy()).reset();
|
||||||
|
|
||||||
// there may still be some runnables from the transfer in the event queue,
|
super.clear();
|
||||||
// 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();
|
|
||||||
contentChanged();
|
contentChanged();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void contentChanged() {
|
private void contentChanged() {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (postProcessor != null)
|
if (postProcessor != null)
|
||||||
postProcessor.cancel(false);
|
postProcessor.cancel(true);
|
||||||
|
|
||||||
postProcessor = new PostProcessor();
|
postProcessor = new PostProcessor();
|
||||||
postProcessor.execute();
|
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.JTable;
|
||||||
import javax.swing.ListSelectionModel;
|
import javax.swing.ListSelectionModel;
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
import javax.swing.event.TableModelEvent;
|
import javax.swing.event.TableModelEvent;
|
||||||
import javax.swing.table.TableColumn;
|
import javax.swing.table.TableColumn;
|
||||||
import javax.swing.table.TableModel;
|
import javax.swing.table.TableModel;
|
||||||
|
@ -93,17 +92,8 @@ class SfvTable extends JTable implements TransferablePolicySupport, Saveable {
|
||||||
public void clear() {
|
public void clear() {
|
||||||
((BackgroundFileTransferablePolicy<?>) getTransferablePolicy()).reset();
|
((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();
|
((ChecksumTableModel) getModel()).clear();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public TransferablePolicy getTransferablePolicy() {
|
public TransferablePolicy getTransferablePolicy() {
|
||||||
|
|
|
@ -3,101 +3,64 @@ package net.sourceforge.filebot.ui.transferablepolicies;
|
||||||
|
|
||||||
|
|
||||||
import java.awt.datatransfer.Transferable;
|
import java.awt.datatransfer.Transferable;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.beans.PropertyChangeSupport;
|
import java.beans.PropertyChangeSupport;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
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 abstract class BackgroundFileTransferablePolicy<V> extends FileTransferablePolicy {
|
||||||
|
|
||||||
public static final String LOADING_PROPERTY = "loading";
|
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
|
@Override
|
||||||
public void handleTransferable(Transferable tr, boolean add) {
|
public void handleTransferable(Transferable tr, boolean add) {
|
||||||
List<File> files = getFilesFromTransferable(tr);
|
List<File> files = getFilesFromTransferable(tr);
|
||||||
|
|
||||||
if (files == null)
|
if ((files == null) || files.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!add)
|
synchronized (this) {
|
||||||
|
reset();
|
||||||
|
|
||||||
|
if (!add) {
|
||||||
clear();
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
execute(new LoadFilesTask(files));
|
worker = new BackgroundWorker(files);
|
||||||
|
worker.addPropertyChangeListener(new BackgroundWorkerListener());
|
||||||
|
worker.execute();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected synchronized void execute(Runnable task) {
|
public synchronized boolean isActive() {
|
||||||
if (executor == null) {
|
return (worker != null) && !worker.isDone();
|
||||||
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 void reset() {
|
public synchronized void reset() {
|
||||||
deactivate(true);
|
if (isActive()) {
|
||||||
setActive(false);
|
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);
|
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;
|
private final List<File> files;
|
||||||
|
|
||||||
|
|
||||||
public LoadFilesTask(List<File> files) {
|
public BackgroundWorker(List<File> files) {
|
||||||
this.files = files;
|
this.files = files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
protected Object doInBackground() {
|
||||||
load(files);
|
load(files);
|
||||||
}
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class ProcessChunksTask implements Runnable {
|
public void publishChunks(V... chunks) {
|
||||||
|
if (!isCancelled()) {
|
||||||
private final V[] chunks;
|
publish(chunks);
|
||||||
|
}
|
||||||
|
|
||||||
public ProcessChunksTask(V[] chunks) {
|
|
||||||
this.chunks = chunks;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
protected void process(List<V> chunks) {
|
||||||
process(Arrays.asList(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) {
|
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
|
||||||
propertyChangeSupport.removePropertyChangeListener(propertyName, 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) {
|
public DefaultThreadFactory(String groupName, int priority, boolean daemon) {
|
||||||
group = new ThreadGroup(groupName);
|
group = new ThreadGroup(groupName);
|
||||||
group.setDaemon(daemon);
|
group.setDaemon(daemon);
|
||||||
|
group.setMaxPriority(priority);
|
||||||
|
|
||||||
this.daemon = daemon;
|
this.daemon = daemon;
|
||||||
this.priority = priority;
|
this.priority = priority;
|
||||||
|
@ -30,12 +31,12 @@ public class DefaultThreadFactory implements ThreadFactory {
|
||||||
|
|
||||||
|
|
||||||
public Thread newThread(Runnable r) {
|
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);
|
thread.setDaemon(daemon);
|
||||||
t.setPriority(priority);
|
thread.setPriority(priority);
|
||||||
|
|
||||||
return t;
|
return thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue