1.1 --- a/src/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java Fri May 15 12:06:22 2009 +0400
1.2 +++ b/src/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java Fri May 15 17:26:45 2009 +0400
1.3 @@ -230,51 +230,46 @@
1.4 }
1.5
1.6 public void run0() {
1.7 - DoChangeContents doChangeContents = ShellFolder.getInvoker().invoke(new Callable<DoChangeContents>() {
1.8 - public DoChangeContents call() throws Exception {
1.9 - FileSystemView fileSystem = filechooser.getFileSystemView();
1.10 + FileSystemView fileSystem = filechooser.getFileSystemView();
1.11
1.12 - File[] list = fileSystem.getFiles(currentDirectory, filechooser.isFileHidingEnabled());
1.13 + File[] list = fileSystem.getFiles(currentDirectory, filechooser.isFileHidingEnabled());
1.14
1.15 - Vector<File> acceptsList = new Vector<File>();
1.16 + if (isInterrupted()) {
1.17 + return;
1.18 + }
1.19
1.20 - if (isInterrupted()) {
1.21 - return null;
1.22 - }
1.23 + final Vector<File> newFileCache = new Vector<File>();
1.24 + Vector<File> newFiles = new Vector<File>();
1.25
1.26 - // run through the file list, add directories and selectable files to fileCache
1.27 - for (File file : list) {
1.28 - if (filechooser.accept(file)) {
1.29 - acceptsList.addElement(file);
1.30 - }
1.31 + // run through the file list, add directories and selectable files to fileCache
1.32 + // Note that this block must be OUTSIDE of Invoker thread because of
1.33 + // deadlock possibility with custom synchronized FileSystemView
1.34 + for (File file : list) {
1.35 + if (filechooser.accept(file)) {
1.36 + boolean isTraversable = filechooser.isTraversable(file);
1.37 +
1.38 + if (isTraversable) {
1.39 + newFileCache.addElement(file);
1.40 + } else if (filechooser.isFileSelectionEnabled()) {
1.41 + newFiles.addElement(file);
1.42 }
1.43
1.44 if (isInterrupted()) {
1.45 - return null;
1.46 + return;
1.47 }
1.48 + }
1.49 + }
1.50
1.51 - // First sort alphabetically by filename
1.52 - sort(acceptsList);
1.53 + // First sort alphabetically by filename
1.54 + sort(newFileCache);
1.55 + sort(newFiles);
1.56
1.57 - Vector<File> newDirectories = new Vector<File>(50);
1.58 - Vector<File> newFiles = new Vector<File>();
1.59 - // run through list grabbing directories in chunks of ten
1.60 - for (int i = 0; i < acceptsList.size(); i++) {
1.61 - File f = acceptsList.elementAt(i);
1.62 - boolean isTraversable = filechooser.isTraversable(f);
1.63 - if (isTraversable) {
1.64 - newDirectories.addElement(f);
1.65 - } else if (!isTraversable && filechooser.isFileSelectionEnabled()) {
1.66 - newFiles.addElement(f);
1.67 - }
1.68 - if (isInterrupted()) {
1.69 - return null;
1.70 - }
1.71 - }
1.72 + newFileCache.addAll(newFiles);
1.73
1.74 - Vector<File> newFileCache = new Vector<File>(newDirectories);
1.75 - newFileCache.addAll(newFiles);
1.76 -
1.77 + // To avoid loads of synchronizations with Invoker and improve performance we
1.78 + // execute the whole block on the COM thread
1.79 + DoChangeContents doChangeContents = ShellFolder.getInvoker().invoke(new Callable<DoChangeContents>() {
1.80 + public DoChangeContents call() throws Exception {
1.81 int newSize = newFileCache.size();
1.82 int oldSize = fileCache.size();
1.83
2.1 --- a/src/share/classes/sun/awt/shell/ShellFolder.java Fri May 15 12:06:22 2009 +0400
2.2 +++ b/src/share/classes/sun/awt/shell/ShellFolder.java Fri May 15 17:26:45 2009 +0400
2.3 @@ -274,45 +274,61 @@
2.4
2.5 // Override File methods
2.6
2.7 - public static void sort(List<? extends File> files) {
2.8 + public static void sort(final List<? extends File> files) {
2.9 if (files == null || files.size() <= 1) {
2.10 return;
2.11 }
2.12
2.13 - // Check that we can use the ShellFolder.sortChildren() method:
2.14 - // 1. All files have the same non-null parent
2.15 - // 2. All files is ShellFolders
2.16 - File commonParent = null;
2.17 + // To avoid loads of synchronizations with Invoker and improve performance we
2.18 + // synchronize the whole code of the sort method once
2.19 + getInvoker().invoke(new Callable<Void>() {
2.20 + public Void call() throws Exception {
2.21 + // Check that we can use the ShellFolder.sortChildren() method:
2.22 + // 1. All files have the same non-null parent
2.23 + // 2. All files is ShellFolders
2.24 + File commonParent = null;
2.25
2.26 - for (File file : files) {
2.27 - File parent = file.getParentFile();
2.28 + for (File file : files) {
2.29 + File parent = file.getParentFile();
2.30
2.31 - if (parent == null || !(file instanceof ShellFolder)) {
2.32 - commonParent = null;
2.33 + if (parent == null || !(file instanceof ShellFolder)) {
2.34 + commonParent = null;
2.35
2.36 - break;
2.37 + break;
2.38 + }
2.39 +
2.40 + if (commonParent == null) {
2.41 + commonParent = parent;
2.42 + } else {
2.43 + if (commonParent != parent && !commonParent.equals(parent)) {
2.44 + commonParent = null;
2.45 +
2.46 + break;
2.47 + }
2.48 + }
2.49 + }
2.50 +
2.51 + if (commonParent instanceof ShellFolder) {
2.52 + ((ShellFolder) commonParent).sortChildren(files);
2.53 + } else {
2.54 + Collections.sort(files, FILE_COMPARATOR);
2.55 + }
2.56 +
2.57 + return null;
2.58 }
2.59 -
2.60 - if (commonParent == null) {
2.61 - commonParent = parent;
2.62 - } else {
2.63 - if (commonParent != parent && !commonParent.equals(parent)) {
2.64 - commonParent = null;
2.65 -
2.66 - break;
2.67 - }
2.68 - }
2.69 - }
2.70 -
2.71 - if (commonParent instanceof ShellFolder) {
2.72 - ((ShellFolder) commonParent).sortChildren(files);
2.73 - } else {
2.74 - Collections.sort(files, FILE_COMPARATOR);
2.75 - }
2.76 + });
2.77 }
2.78
2.79 - public void sortChildren(List<? extends File> files) {
2.80 - Collections.sort(files, FILE_COMPARATOR);
2.81 + public void sortChildren(final List<? extends File> files) {
2.82 + // To avoid loads of synchronizations with Invoker and improve performance we
2.83 + // synchronize the whole code of the sort method once
2.84 + getInvoker().invoke(new Callable<Void>() {
2.85 + public Void call() throws Exception {
2.86 + Collections.sort(files, FILE_COMPARATOR);
2.87 +
2.88 + return null;
2.89 + }
2.90 + });
2.91 }
2.92
2.93 public boolean isAbsolute() {
3.1 --- a/src/windows/classes/sun/awt/shell/Win32ShellFolder2.java Fri May 15 12:06:22 2009 +0400
3.2 +++ b/src/windows/classes/sun/awt/shell/Win32ShellFolder2.java Fri May 15 17:26:45 2009 +0400
3.3 @@ -527,7 +527,7 @@
3.4 /**
3.5 * @return Whether this is a file system shell folder
3.6 */
3.7 - public synchronized boolean isFileSystem() {
3.8 + public boolean isFileSystem() {
3.9 if (cachedIsFileSystem == null) {
3.10 cachedIsFileSystem = hasAttribute(ATTRIB_FILESYSTEM);
3.11 }
3.12 @@ -543,8 +543,8 @@
3.13 public Boolean call() throws Exception {
3.14 // Caching at this point doesn't seem to be cost efficient
3.15 return (getAttributes0(getParentIShellFolder(),
3.16 - getRelativePIDL(), attribute)
3.17 - & attribute) != 0;
3.18 + getRelativePIDL(), attribute)
3.19 + & attribute) != 0;
3.20 }
3.21 });
3.22 }
3.23 @@ -761,7 +761,7 @@
3.24 /**
3.25 * @return Whether this shell folder is a link
3.26 */
3.27 - public synchronized boolean isLink() {
3.28 + public boolean isLink() {
3.29 if (cachedIsLink == null) {
3.30 cachedIsLink = hasAttribute(ATTRIB_LINK);
3.31 }
3.32 @@ -1160,8 +1160,16 @@
3.33 private static native int compareIDsByColumn(long pParentIShellFolder, long pidl1, long pidl2, int columnIdx);
3.34
3.35
3.36 - public void sortChildren(List<? extends File> files) {
3.37 - Collections.sort(files, new ColumnComparator(getIShellFolder(), 0));
3.38 + public void sortChildren(final List<? extends File> files) {
3.39 + // To avoid loads of synchronizations with Invoker and improve performance we
3.40 + // synchronize the whole code of the sort method once
3.41 + getInvoker().invoke(new Callable<Void>() {
3.42 + public Void call() throws Exception {
3.43 + Collections.sort(files, new ColumnComparator(getIShellFolder(), 0));
3.44 +
3.45 + return null;
3.46 + }
3.47 + });
3.48 }
3.49
3.50 private static class ColumnComparator implements Comparator<File> {
4.1 --- a/src/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java Fri May 15 12:06:22 2009 +0400
4.2 +++ b/src/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java Fri May 15 17:26:45 2009 +0400
4.3 @@ -478,21 +478,22 @@
4.4
4.5 public <T> T invoke(Callable<T> task) {
4.6 try {
4.7 - T result;
4.8 if (Thread.currentThread() == comThread) {
4.9 // if it's already called from the COM
4.10 // thread, we don't need to delegate the task
4.11 - result = task.call();
4.12 + return task.call();
4.13 } else {
4.14 - Future<T> future = submit(task);
4.15 - try {
4.16 - result = future.get();
4.17 - } catch (InterruptedException e) {
4.18 - result = null;
4.19 - future.cancel(true);
4.20 + while (true) {
4.21 + Future<T> future = submit(task);
4.22 +
4.23 + try {
4.24 + return future.get();
4.25 + } catch (InterruptedException e) {
4.26 + // Repeat the attempt
4.27 + future.cancel(true);
4.28 + }
4.29 }
4.30 }
4.31 - return result;
4.32 } catch (Exception e) {
4.33 Throwable cause = (e instanceof ExecutionException) ? e.getCause() : e;
4.34 if (cause instanceof RuntimeException) {
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/test/javax/swing/JFileChooser/6713352/bug6713352.java Fri May 15 17:26:45 2009 +0400
5.3 @@ -0,0 +1,99 @@
5.4 +/*
5.5 + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
5.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5.7 + *
5.8 + * This code is free software; you can redistribute it and/or modify it
5.9 + * under the terms of the GNU General Public License version 2 only, as
5.10 + * published by the Free Software Foundation.
5.11 + *
5.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
5.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
5.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
5.15 + * version 2 for more details (a copy is included in the LICENSE file that
5.16 + * accompanied this code).
5.17 + *
5.18 + * You should have received a copy of the GNU General Public License version
5.19 + * 2 along with this work; if not, write to the Free Software Foundation,
5.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
5.21 + *
5.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
5.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
5.24 + * have any questions.
5.25 + */
5.26 +
5.27 +/* @test
5.28 + @bug 6713352
5.29 + @summary Deadlock in JFileChooser with synchronized custom FileSystemView
5.30 + @author Pavel Porvatov
5.31 + @run main bug6713352
5.32 +*/
5.33 +
5.34 +import sun.awt.shell.ShellFolder;
5.35 +
5.36 +import javax.swing.*;
5.37 +import javax.swing.filechooser.FileSystemView;
5.38 +import java.io.File;
5.39 +import java.io.FileNotFoundException;
5.40 +import java.io.IOException;
5.41 +
5.42 +public class bug6713352 {
5.43 + public static void main(String[] args) throws Exception {
5.44 + SwingUtilities.invokeAndWait(new Runnable() {
5.45 + public void run() {
5.46 + MyFileSystemView systemView = new MyFileSystemView();
5.47 +
5.48 + synchronized (systemView) { // Get SystemView lock
5.49 + new JFileChooser(systemView);
5.50 +
5.51 + // Wait a little bit. BasicDirectoryModel will lock Invoker and stop on
5.52 + // the bug6713352.MyFileSystemView.getFiles() method
5.53 + try {
5.54 + Thread.sleep(5000);
5.55 + } catch (InterruptedException e) {
5.56 + throw new RuntimeException(e);
5.57 + }
5.58 +
5.59 + try {
5.60 + System.out.println("Try to get Invokers lock");
5.61 +
5.62 + ShellFolder.getShellFolder(new File("c:/")).listFiles(true);
5.63 + } catch (FileNotFoundException e) {
5.64 + throw new RuntimeException(e);
5.65 + }
5.66 + }
5.67 +
5.68 + // To avoid RejectedExecutionException in BasicDirectoryModel wait a second
5.69 + try {
5.70 + Thread.sleep(1000);
5.71 + } catch (InterruptedException e) {
5.72 + throw new RuntimeException(e);
5.73 + }
5.74 + }
5.75 + });
5.76 + }
5.77 +
5.78 + private static class MyFileSystemView extends FileSystemView {
5.79 +
5.80 + public File createNewFolder(File containingDir) throws IOException {
5.81 + return null;
5.82 + }
5.83 +
5.84 + public File[] getFiles(File dir, boolean useFileHiding) {
5.85 + System.out.println("getFiles start");
5.86 +
5.87 + File[] result;
5.88 +
5.89 + synchronized (this) {
5.90 + result = super.getFiles(dir, useFileHiding);
5.91 + }
5.92 +
5.93 + System.out.println("getFiles finished");
5.94 +
5.95 + return result;
5.96 + }
5.97 +
5.98 + public synchronized Boolean isTraversable(File f) {
5.99 + return super.isTraversable(f);
5.100 + }
5.101 + }
5.102 +}