Skip to content

Commit e66788c

Browse files
committed
8325179: Race in BasicDirectoryModel.validateFileCache
8238169: BasicDirectoryModel getDirectories and DoChangeContents.run can deadlock Reviewed-by: prr, tr, aturbanov, serb
1 parent 1496b5d commit e66788c

File tree

1 file changed

+48
-52
lines changed

1 file changed

+48
-52
lines changed

src/java.desktop/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java

+48-52
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,13 @@ public void propertyChange(PropertyChangeEvent e) {
9898
* This method is used to interrupt file loading thread.
9999
*/
100100
public void invalidateFileCache() {
101-
if (filesLoader != null) {
102-
filesLoader.loadThread.interrupt();
103-
filesLoader.cancelRunnables();
104-
filesLoader = null;
101+
synchronized (this) {
102+
if (filesLoader != null) {
103+
filesLoader.loadThread.interrupt();
104+
filesLoader = null;
105+
// Increment fetch ID to invalidate pending DoChangeContents
106+
fetchID.incrementAndGet();
107+
}
105108
}
106109
}
107110

@@ -156,14 +159,15 @@ public void validateFileCache() {
156159
if (currentDirectory == null) {
157160
return;
158161
}
159-
if (filesLoader != null) {
160-
filesLoader.loadThread.interrupt();
161-
filesLoader.cancelRunnables();
162-
}
162+
synchronized (this) {
163+
if (filesLoader != null) {
164+
filesLoader.loadThread.interrupt();
165+
}
163166

164-
int fid = fetchID.incrementAndGet();
165-
setBusy(true, fid);
166-
filesLoader = new FilesLoader(currentDirectory, fid);
167+
int fid = fetchID.incrementAndGet();
168+
setBusy(true, fid);
169+
filesLoader = new FilesLoader(currentDirectory, fid);
170+
}
167171
}
168172

169173
/**
@@ -276,7 +280,6 @@ private final class FilesLoader implements Runnable {
276280
private final boolean fileSelectionEnabled;
277281
private final int fid;
278282
private final File currentDirectory;
279-
private volatile DoChangeContents runnable;
280283
private final Thread loadThread;
281284

282285
private FilesLoader(File currentDirectory, int fid) {
@@ -297,22 +300,20 @@ public void run() {
297300
}
298301

299302
private void run0() {
300-
FileSystemView fileSystem = fileSystemView;
301-
302303
if (loadThread.isInterrupted()) {
303304
return;
304305
}
305306

306-
File[] list = fileSystem.getFiles(currentDirectory, useFileHiding);
307+
File[] list = fileSystemView.getFiles(currentDirectory, useFileHiding);
307308

308309
if (loadThread.isInterrupted()) {
309310
return;
310311
}
311312

312313
final Vector<File> newFileCache = new Vector<File>();
313-
Vector<File> newFiles = new Vector<File>();
314+
final Vector<File> newFiles = new Vector<File>();
314315

315-
// run through the file list, add directories and selectable files to fileCache
316+
// Run through the file list, add directories and selectable files to fileCache
316317
// Note that this block must be OUTSIDE of Invoker thread because of
317318
// deadlock possibility with custom synchronized FileSystemView
318319
for (File file : list) {
@@ -339,7 +340,7 @@ private void run0() {
339340

340341
// To avoid loads of synchronizations with Invoker and improve performance we
341342
// execute the whole block on the COM thread
342-
runnable = ShellFolder.invoke(new Callable<DoChangeContents>() {
343+
DoChangeContents runnable = ShellFolder.invoke(new Callable<DoChangeContents>() {
343344
public DoChangeContents call() {
344345
synchronized (fileCache) {
345346
int newSize = newFileCache.size();
@@ -395,7 +396,7 @@ public DoChangeContents call() {
395396
}
396397
if (!fileCache.equals(newFileCache)) {
397398
if (loadThread.isInterrupted()) {
398-
cancelRunnables();
399+
return null;
399400
}
400401
return new DoChangeContents(newFileCache, 0, fileCache, 0, fid);
401402
}
@@ -408,12 +409,6 @@ public DoChangeContents call() {
408409
SwingUtilities.invokeLater(runnable);
409410
}
410411
}
411-
412-
private void cancelRunnables() {
413-
if (runnable != null) {
414-
runnable.cancel();
415-
}
416-
}
417412
}
418413

419414

@@ -522,45 +517,46 @@ public void run() {
522517
private final class DoChangeContents implements Runnable {
523518
private final List<File> addFiles;
524519
private final List<File> remFiles;
525-
private boolean doFire = true;
526520
private final int fid;
527-
private int addStart = 0;
528-
private int remStart = 0;
521+
private final int addStart;
522+
private final int remStart;
529523

530-
DoChangeContents(List<File> addFiles, int addStart, List<File> remFiles,
531-
int remStart, int fid) {
524+
private DoChangeContents(List<File> addFiles, int addStart,
525+
List<File> remFiles, int remStart,
526+
int fid) {
532527
this.addFiles = addFiles;
533528
this.addStart = addStart;
534529
this.remFiles = remFiles;
535530
this.remStart = remStart;
536531
this.fid = fid;
537532
}
538533

539-
synchronized void cancel() {
540-
doFire = false;
541-
}
534+
@Override
535+
public void run() {
536+
if (fetchID.get() != fid) {
537+
return;
538+
}
542539

543-
public synchronized void run() {
544-
if (fetchID.get() == fid && doFire) {
545-
int remSize = (remFiles == null) ? 0 : remFiles.size();
546-
int addSize = (addFiles == null) ? 0 : addFiles.size();
547-
synchronized(fileCache) {
548-
if (remSize > 0) {
549-
fileCache.removeAll(remFiles);
550-
}
551-
if (addSize > 0) {
552-
fileCache.addAll(addStart, addFiles);
553-
}
554-
files = null;
555-
directories = null;
540+
final int remSize = (remFiles == null) ? 0 : remFiles.size();
541+
final int addSize = (addFiles == null) ? 0 : addFiles.size();
542+
final int cacheSize;
543+
synchronized (fileCache) {
544+
if (remSize > 0) {
545+
fileCache.removeAll(remFiles);
556546
}
557-
if (remSize > 0 && addSize == 0) {
558-
fireIntervalRemoved(BasicDirectoryModel.this, remStart, remStart + remSize - 1);
559-
} else if (addSize > 0 && remSize == 0 && addStart + addSize <= fileCache.size()) {
560-
fireIntervalAdded(BasicDirectoryModel.this, addStart, addStart + addSize - 1);
561-
} else {
562-
fireContentsChanged();
547+
if (addSize > 0) {
548+
fileCache.addAll(addStart, addFiles);
563549
}
550+
files = null;
551+
directories = null;
552+
cacheSize = fileCache.size();
553+
}
554+
if (remSize > 0 && addSize == 0) {
555+
fireIntervalRemoved(BasicDirectoryModel.this, remStart, remStart + remSize - 1);
556+
} else if (addSize > 0 && remSize == 0 && addStart + addSize <= cacheSize) {
557+
fireIntervalAdded(BasicDirectoryModel.this, addStart, addStart + addSize - 1);
558+
} else {
559+
fireContentsChanged();
564560
}
565561
}
566562
}

0 commit comments

Comments
 (0)