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.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@ -231,7 +232,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
}
// 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)));
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
return selectDialog.getSelectedValue();
});
};
synchronized (selectionMemory) {
if (selectionMemory.containsKey(query)) {
@ -277,14 +278,11 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
}
// allow only one select dialog at a time
synchronized (INPUT_DIALOG_LOCK) {
SwingUtilities.invokeAndWait(showSelectDialog);
SearchResult userSelection = showSelectDialog.get();
SearchResult userSelection = showInputDialog(showSelectDialog);
// remember selected value
selectionMemory.put(query, userSelection);
return userSelection;
}
// remember selected value
selectionMemory.put(query, userSelection);
return userSelection;
}
}

View File

@ -29,18 +29,16 @@ import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
import java.util.logging.Level;
import java.util.prefs.Preferences;
import javax.swing.Action;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import net.filebot.similarity.Match;
import net.filebot.similarity.NameSimilarityMetric;
@ -340,7 +338,7 @@ class MovieMatcher implements AutoCompleteMatcher {
}
// show selection dialog on EDT
RunnableFuture<Movie> showSelectDialog = new FutureTask<Movie>(() -> {
Callable<Movie> showSelectDialog = () -> {
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));
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
return selectDialog.getSelectedValue();
});
};
// allow only one select dialog at a time
synchronized (selectionMemory) {
@ -389,13 +387,12 @@ class MovieMatcher implements AutoCompleteMatcher {
return null;
}
synchronized (INPUT_DIALOG_LOCK) {
SwingUtilities.invokeAndWait(showSelectDialog);
// allow only one select dialog at a time
Movie userSelection = showInputDialog(showSelectDialog);
// cache selected value
selectionMemory.put(selectionKey, showSelectDialog.get());
return showSelectDialog.get();
}
// cache selected value
selectionMemory.put(selectionKey, userSelection);
return userSelection;
}
}

View File

@ -31,7 +31,9 @@ import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.regex.Pattern;
@ -226,7 +228,39 @@ public final class SwingUI {
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);
if (input == null || input.isEmpty()) {
return emptyList();
@ -252,23 +286,6 @@ public final class SwingUI {
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) {
if (component instanceof Window)
return (Window) component;