Make sure to not lock on Swing / AWT Window object

@see https://www.filebot.net/forums/viewtopic.php?f=11&t=5259&p=29747#p35519
This commit is contained in:
Reinhard Pointner 2018-08-30 17:15:16 +07:00
parent b9572f4501
commit 9d15ac34b4
3 changed files with 50 additions and 38 deletions

View File

@ -25,6 +25,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -231,7 +232,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
} }
// show selection dialog on EDT // show selection dialog on EDT
RunnableFuture<SearchResult> showSelectDialog = new FutureTask<SearchResult>(() -> { Callable<SearchResult> showSelectDialog = () -> {
JLabel header = new JLabel(getQueryInputMessage("Failed to identify some of the following files:", null, getFilesForQuery(files, query))); JLabel header = new JLabel(getQueryInputMessage("Failed to identify some of the following files:", null, getFilesForQuery(files, query)));
header.setBorder(createCompoundBorder(createTitledBorder(""), createEmptyBorder(3, 3, 3, 3))); header.setBorder(createCompoundBorder(createTitledBorder(""), createEmptyBorder(3, 3, 3, 3)));
@ -261,7 +262,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
// selected value or null if the dialog was canceled by the user // selected value or null if the dialog was canceled by the user
return selectDialog.getSelectedValue(); return selectDialog.getSelectedValue();
}); };
synchronized (selectionMemory) { synchronized (selectionMemory) {
if (selectionMemory.containsKey(query)) { if (selectionMemory.containsKey(query)) {
@ -277,16 +278,13 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
} }
// allow only one select dialog at a time // allow only one select dialog at a time
synchronized (INPUT_DIALOG_LOCK) { SearchResult userSelection = showInputDialog(showSelectDialog);
SwingUtilities.invokeAndWait(showSelectDialog);
SearchResult userSelection = showSelectDialog.get();
// remember selected value // remember selected value
selectionMemory.put(query, userSelection); selectionMemory.put(query, userSelection);
return userSelection; return userSelection;
} }
} }
}
protected Collection<File> getFilesForQuery(Collection<File> files, String query) { protected Collection<File> getFilesForQuery(Collection<File> files, String query) {
Pattern pattern = Pattern.compile(query.isEmpty() ? ".+" : normalizePunctuation(query).replaceAll("\\W+", ".+"), Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CHARACTER_CLASS); Pattern pattern = Pattern.compile(query.isEmpty() ? ".+" : normalizePunctuation(query).replaceAll("\\W+", ".+"), Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CHARACTER_CLASS);

View File

@ -29,18 +29,16 @@ import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
import javax.swing.Action; import javax.swing.Action;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import net.filebot.similarity.Match; import net.filebot.similarity.Match;
import net.filebot.similarity.NameSimilarityMetric; import net.filebot.similarity.NameSimilarityMetric;
@ -340,7 +338,7 @@ class MovieMatcher implements AutoCompleteMatcher {
} }
// show selection dialog on EDT // show selection dialog on EDT
RunnableFuture<Movie> showSelectDialog = new FutureTask<Movie>(() -> { Callable<Movie> showSelectDialog = () -> {
String query = fileQuery.length() >= 2 || folderQuery.length() <= 2 ? fileQuery : folderQuery; String query = fileQuery.length() >= 2 || folderQuery.length() <= 2 ? fileQuery : folderQuery;
JLabel header = new JLabel(getQueryInputMessage("Failed to identify some of the following files:", null, movieFile)); JLabel header = new JLabel(getQueryInputMessage("Failed to identify some of the following files:", null, movieFile));
header.setBorder(createCompoundBorder(createTitledBorder(""), createEmptyBorder(3, 3, 3, 3))); header.setBorder(createCompoundBorder(createTitledBorder(""), createEmptyBorder(3, 3, 3, 3)));
@ -372,7 +370,7 @@ class MovieMatcher implements AutoCompleteMatcher {
// selected value or null if the dialog was canceled by the user // selected value or null if the dialog was canceled by the user
return selectDialog.getSelectedValue(); return selectDialog.getSelectedValue();
}); };
// allow only one select dialog at a time // allow only one select dialog at a time
synchronized (selectionMemory) { synchronized (selectionMemory) {
@ -389,13 +387,12 @@ class MovieMatcher implements AutoCompleteMatcher {
return null; return null;
} }
synchronized (INPUT_DIALOG_LOCK) { // allow only one select dialog at a time
SwingUtilities.invokeAndWait(showSelectDialog); Movie userSelection = showInputDialog(showSelectDialog);
// cache selected value // cache selected value
selectionMemory.put(selectionKey, showSelectDialog.get()); selectionMemory.put(selectionKey, userSelection);
return showSelectDialog.get(); return userSelection;
}
} }
} }

View File

@ -31,7 +31,9 @@ import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -226,7 +228,39 @@ public final class SwingUI {
return (frame.getExtendedState() & Frame.MAXIMIZED_BOTH) != 0; return (frame.getExtendedState() & Frame.MAXIMIZED_BOTH) != 0;
} }
public static List<String> showMultiValueInputDialog(Object message, String initialValue, String title, Component parent) { private static final ReentrantLock INPUT_DIALOG_LOCK = new ReentrantLock(true); // use fair lock to display dialogs in FIFO order
public static <T> T showInputDialog(Callable<T> callable) throws Exception {
Object[] value = { null };
// display only one dialog at a time, one after another, in the correct sequence
INPUT_DIALOG_LOCK.lock();
try {
if (SwingUtilities.isEventDispatchThread()) {
value[0] = callable.call();
} else {
SwingUtilities.invokeAndWait(() -> {
try {
value[0] = callable.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
} finally {
INPUT_DIALOG_LOCK.unlock();
}
return (T) value[0];
}
public static String showInputDialog(Object message, String initialValue, String title, Component parent) throws Exception {
return showInputDialog(() -> {
return (String) JOptionPane.showInputDialog(parent, message, title, PLAIN_MESSAGE, null, null, initialValue);
});
}
public static List<String> showMultiValueInputDialog(Object message, String initialValue, String title, Component parent) throws Exception {
String input = showInputDialog(message, initialValue, title, parent); String input = showInputDialog(message, initialValue, title, parent);
if (input == null || input.isEmpty()) { if (input == null || input.isEmpty()) {
return emptyList(); return emptyList();
@ -252,23 +286,6 @@ public final class SwingUI {
return singletonList(input); return singletonList(input);
} }
public static final Object INPUT_DIALOG_LOCK = new Object();
public static String showInputDialog(Object message, String initialValue, String title, Component parent) {
StringBuilder buffer = new StringBuilder();
synchronized (INPUT_DIALOG_LOCK) {
runOnEventDispatchThread(() -> {
Object value = JOptionPane.showInputDialog(parent, message, title, PLAIN_MESSAGE, null, null, initialValue);
if (value != null) {
buffer.append(value.toString().trim());
}
});
}
return buffer.length() == 0 ? null : buffer.toString();
}
public static Window getWindow(Object component) { public static Window getWindow(Object component) {
if (component instanceof Window) if (component instanceof Window)
return (Window) component; return (Window) component;