-
Bug
-
Resolution: Not an Issue
-
P4
-
None
-
8u192
-
x86_64
-
windows_10
ADDITIONAL SYSTEM INFORMATION :
windows 10.0.16299
java version "1.8.0_192"
Java(TM) SE Runtime Environment (build 1.8.0_192-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.192-b12, mixed mode)
A DESCRIPTION OF THE PROBLEM :
If the traversed directory is deleted in the postVisitDirectory callback, the directory will get into wrong state. It is not actually deleted and becomes inaccessible, such as can not open in Windows Explorer. Calling Files. delete() again on this directory will throws java.nio.file.AccessDeniedException. When the process is over, it will return to normal.
This directory's error state can be reproduced in another way, first by creating DirectoryStream through Files. newDirectoryStream(dir) call, and then deleting dir without closing DirectoryStream. The dir enters the same state as when deleted in postVisitDirectory callback.
So the most doubtful thing is that Files. walkFileTree() did not close the DirectoryStream that has been used, but I saw that DirectoryStream#close() was called in FileTreeWalker#next(). Finally i find the problem was that Windows DirectoryStream#close() was not synchronized. DirectoryStream was not actually closed when deleting directories in postVisitDirectory. The bug will not recur after some delay is added before the Files.delete() call.
REGRESSION : Last worked in version 8u192
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
mkdir : C:/1/2/3
runcode
public static void main(String[] args) throws Exception {
Files.walkFileTree(Paths.get("C:/1"), Collections.emptySet(), Integer.MAX_VALUE,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
return super.postVisitDirectory(dir, exc);
}
});
}
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
C:/1 has been deleted
ACTUAL -
Program throws java.nio.file.DirectoryNotEmptyException. And only C:/1/2/3 has been deleted, C:/1/2 still exists.
The reason is that when C:/1/2/3 is deleteing from the postVisitDirectory, the DirectoryStream associated with it is not actually closed, so the directory entry wrong state and is not deleted. When deleteing C:/1/2 , the DirectoryNotEmptyException will be thrown.
---------- BEGIN SOURCE ----------
public static void main(String[] args) throws Exception {
Files.walkFileTree(Paths.get("C:/1"), Collections.emptySet(), Integer.MAX_VALUE,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
return super.postVisitDirectory(dir, exc);
}
});
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Add some delay before delete dir.
public static void main(String[] args) throws Exception {
String property = System.getProperty("os.name");
boolean isWin = property != null && property.toLowerCase().startsWith("win");
Files.walkFileTree(Paths.get("C:/1"), Collections.emptySet(), Integer.MAX_VALUE,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
if (isWin) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
}
Files.delete(dir);
return super.postVisitDirectory(dir, exc);
}
});
}
FREQUENCY : always
windows 10.0.16299
java version "1.8.0_192"
Java(TM) SE Runtime Environment (build 1.8.0_192-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.192-b12, mixed mode)
A DESCRIPTION OF THE PROBLEM :
If the traversed directory is deleted in the postVisitDirectory callback, the directory will get into wrong state. It is not actually deleted and becomes inaccessible, such as can not open in Windows Explorer. Calling Files. delete() again on this directory will throws java.nio.file.AccessDeniedException. When the process is over, it will return to normal.
This directory's error state can be reproduced in another way, first by creating DirectoryStream through Files. newDirectoryStream(dir) call, and then deleting dir without closing DirectoryStream. The dir enters the same state as when deleted in postVisitDirectory callback.
So the most doubtful thing is that Files. walkFileTree() did not close the DirectoryStream that has been used, but I saw that DirectoryStream#close() was called in FileTreeWalker#next(). Finally i find the problem was that Windows DirectoryStream#close() was not synchronized. DirectoryStream was not actually closed when deleting directories in postVisitDirectory. The bug will not recur after some delay is added before the Files.delete() call.
REGRESSION : Last worked in version 8u192
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
mkdir : C:/1/2/3
runcode
public static void main(String[] args) throws Exception {
Files.walkFileTree(Paths.get("C:/1"), Collections.emptySet(), Integer.MAX_VALUE,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
return super.postVisitDirectory(dir, exc);
}
});
}
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
C:/1 has been deleted
ACTUAL -
Program throws java.nio.file.DirectoryNotEmptyException. And only C:/1/2/3 has been deleted, C:/1/2 still exists.
The reason is that when C:/1/2/3 is deleteing from the postVisitDirectory, the DirectoryStream associated with it is not actually closed, so the directory entry wrong state and is not deleted. When deleteing C:/1/2 , the DirectoryNotEmptyException will be thrown.
---------- BEGIN SOURCE ----------
public static void main(String[] args) throws Exception {
Files.walkFileTree(Paths.get("C:/1"), Collections.emptySet(), Integer.MAX_VALUE,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
return super.postVisitDirectory(dir, exc);
}
});
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Add some delay before delete dir.
public static void main(String[] args) throws Exception {
String property = System.getProperty("os.name");
boolean isWin = property != null && property.toLowerCase().startsWith("win");
Files.walkFileTree(Paths.get("C:/1"), Collections.emptySet(), Integer.MAX_VALUE,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
if (isWin) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
}
Files.delete(dir);
return super.postVisitDirectory(dir, exc);
}
});
}
FREQUENCY : always