Skip to content

Commit 5f7329d

Browse files
committed
Fix bug that IndexingRescanner generates events outside the watch scope
1 parent f81df21 commit 5f7329d

File tree

1 file changed

+33
-10
lines changed

1 file changed

+33
-10
lines changed

src/main/java/engineering/swat/watch/impl/overflows/IndexingRescanner.java

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
import java.nio.file.Path;
3333
import java.nio.file.attribute.BasicFileAttributes;
3434
import java.nio.file.attribute.FileTime;
35+
import java.util.ArrayDeque;
36+
import java.util.Deque;
3537
import java.util.HashSet;
3638
import java.util.Map;
3739
import java.util.Set;
@@ -82,20 +84,29 @@ protected MemorylessRescanner.Generator newGenerator(Path path, WatchScope scope
8284
}
8385

8486
protected class Generator extends MemorylessRescanner.Generator {
85-
// Field to keep track of the paths that are visited during the current
86-
// rescan. After the visit, the `DELETED` events that happened since the
87-
// previous rescan can be approximated.
88-
private Set<Path> visited = new HashSet<>();
87+
// Field to keep track of (a stack of) the paths that are visited during
88+
// the current rescan (one frame for each nested subdirectory), to
89+
// approximate `DELETED` events that happened since the previous rescan.
90+
// Instances of this class are supposed to be used non-concurrently, so
91+
// no synchronization to access this field is needed.
92+
private Deque<Set<Path>> visited = new ArrayDeque<>();
8993

9094
public Generator(Path path, WatchScope scope) {
9195
super(path, scope);
96+
this.visited.push(new HashSet<>());
97+
}
98+
99+
private <T> void addToPeeked(Deque<Set<T>> deque, T t) {
100+
var peeked = deque.peek();
101+
if (peeked != null) {
102+
peeked.add(t);
103+
}
92104
}
93105

94106
// -- MemorylessRescanner.Generator --
95107

96108
@Override
97109
protected void generateEvents(Path path, BasicFileAttributes attrs) {
98-
visited.add(path);
99110
var lastModifiedTimeOld = index.get(path);
100111
var lastModifiedTimeNew = attrs.lastModifiedTime();
101112

@@ -111,14 +122,26 @@ else if (lastModifiedTimeOld.compareTo(lastModifiedTimeNew) < 0) {
111122
}
112123
}
113124

125+
@Override
126+
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
127+
addToPeeked(visited, dir);
128+
visited.push(new HashSet<>());
129+
return super.preVisitDirectory(dir, attrs);
130+
}
131+
132+
@Override
133+
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
134+
addToPeeked(visited, file);
135+
return super.visitFile(file, attrs);
136+
}
137+
114138
@Override
115139
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
116-
// If the visitor is back at the root of the rescan, then the time
117-
// is right to issue `DELETED` events based on the set of `visited`
118-
// paths.
119-
if (dir.equals(path)) {
140+
// Issue `DELETED` events based on the set of paths visited in `dir`
141+
var visitedInDir = visited.pop();
142+
if (visitedInDir != null) {
120143
for (var p : index.keySet()) {
121-
if (p.startsWith(path) && !visited.contains(p)) {
144+
if (dir.equals(p.getParent()) && !visitedInDir.contains(p)) {
122145
events.add(new WatchEvent(WatchEvent.Kind.DELETED, p));
123146
}
124147
}

0 commit comments

Comments
 (0)