* Simplyfied BackgroundTransferablePolicy again (no queuing again)

* solves the "add after clear" concurrency problem when adding really lots of files
This commit is contained in:
Reinhard Pointner 2008-03-27 21:44:48 +00:00
parent 2026c60b1d
commit 02057b3056
4 changed files with 79 additions and 143 deletions

View File

@ -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 {
} }
} }
} }

View File

@ -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() {

View File

@ -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);
}
}
}
} }

View File

@ -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;
} }
} }