Skip to content

Commit 385db76

Browse files
committed
Add code to close child watches when their directories no longer exist (after an overflow event)
1 parent 1ab8f29 commit 385db76

File tree

1 file changed

+34
-19
lines changed

1 file changed

+34
-19
lines changed

src/main/java/engineering/swat/watch/impl/jdk/JDKFileTreeWatch.java

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.io.IOException;
3030
import java.nio.file.Files;
3131
import java.nio.file.Path;
32+
import java.util.HashSet;
3233
import java.util.Map;
3334
import java.util.concurrent.ConcurrentHashMap;
3435
import java.util.concurrent.Executor;
@@ -97,22 +98,25 @@ protected WatchEvent translate(java.nio.file.WatchEvent<?> jdkEvent) {
9798
}
9899

99100
/**
100-
* Event handler that updates the child watches according to the following
101-
* rules: (a) when an overflow happens, it's propagated to each existing
102-
* child watch; (b) when a subdirectory creation happens, a new child watch
103-
* is opened for that subdirectory; (c) when a subdirectory deletion
104-
* happens, an existing child watch is closed for that subdirectory.
101+
* Event handler that asynchronously (using {@link JDKBaseWatch#exec})
102+
* updates the child watches according to the following rules: (a) when an
103+
* overflow happens, the directory is rescanned, new child watches for
104+
* created subdirectories are opened, existing child watches for deleted
105+
* subdirectories are closed, and the overflow is propagated to each child
106+
* watch; (b) when a subdirectory creation happens, a new child watch is
107+
* opened for that subdirectory; (c) when a subdirectory deletion happens,
108+
* an existing child watch is closed for that subdirectory.
105109
*/
106110
private class AsyncChildWatchesUpdater implements BiConsumer<EventHandlingWatch, WatchEvent> {
107111
@Override
108112
public void accept(EventHandlingWatch watch, WatchEvent event) {
109113
exec.execute(() -> {
110-
switch (event.getKind()) {
111-
case OVERFLOW: acceptOverflow(); break;
112-
case CREATED: getFileNameAndThen(event, this::acceptCreated); break;
113-
case DELETED: getFileNameAndThen(event, this::acceptDeleted); break;
114-
case MODIFIED: break;
115-
}
114+
switch (event.getKind()) {
115+
case OVERFLOW: acceptOverflow(); break;
116+
case CREATED: getFileNameAndThen(event, this::acceptCreated); break;
117+
case DELETED: getFileNameAndThen(event, this::acceptDeleted); break;
118+
case MODIFIED: break;
119+
}
116120
});
117121
}
118122

@@ -126,7 +130,7 @@ private void getFileNameAndThen(WatchEvent event, Consumer<Path> consumer) {
126130
}
127131

128132
private void acceptOverflow() {
129-
openChildWatches();
133+
openAndCloseChildWatches();
130134
for (var childWatch : childWatches.values()) {
131135
reportOverflowTo(childWatch);
132136
}
@@ -143,11 +147,7 @@ private void acceptCreated(Path child) {
143147
}
144148

145149
private void acceptDeleted(Path child) {
146-
try {
147-
closeChildWatch(child);
148-
} catch (IOException e) {
149-
logger.error("Could not close (nested) file tree watch for: {} ({})", path.resolve(child), e);
150-
}
150+
tryCloseChildWatch(child);
151151
}
152152

153153
private void reportOverflowTo(JDKFileTreeWatch childWatch) {
@@ -157,11 +157,14 @@ private void reportOverflowTo(JDKFileTreeWatch childWatch) {
157157
}
158158
}
159159

160-
private void openChildWatches() {
160+
private void openAndCloseChildWatches() {
161+
var toBeClosed = new HashSet<>(childWatches.keySet());
162+
161163
try (var children = Files.find(path, 1, (p, attrs) -> p != path && attrs.isDirectory())) {
162164
children.forEach(p -> {
163165
var child = p.getFileName();
164166
if (child != null) {
167+
toBeClosed.remove(child);
165168
openChildWatch(child);
166169
} else {
167170
logger.error("File tree watch (for: {}) could not open a child watch for: {}", path, p);
@@ -170,6 +173,10 @@ private void openChildWatches() {
170173
} catch (IOException e) {
171174
logger.error("File tree watch (for: {}) could not iterate over its children ({})", path, e);
172175
}
176+
177+
for (var child : toBeClosed) {
178+
tryCloseChildWatch(child);
179+
}
173180
}
174181

175182
private JDKFileTreeWatch openChildWatch(Path child) {
@@ -186,6 +193,14 @@ private JDKFileTreeWatch openChildWatch(Path child) {
186193
return childWatch;
187194
}
188195

196+
private void tryCloseChildWatch(Path child) {
197+
try {
198+
closeChildWatch(child);
199+
} catch (IOException e) {
200+
logger.error("Could not close (nested) file tree watch for: {} ({})", path.resolve(child), e);
201+
}
202+
}
203+
189204
private void closeChildWatch(Path child) throws IOException {
190205
assert !child.isAbsolute();
191206

@@ -242,7 +257,7 @@ public synchronized void close() throws IOException {
242257
@Override
243258
protected synchronized void start() throws IOException {
244259
internal.open();
245-
openChildWatches();
260+
openAndCloseChildWatches();
246261
// There's no need to report an overflow event, because `internal` was
247262
// opened *before* the file system was accessed to fetch children. Thus,
248263
// if a new directory is created while this method is running, then at

0 commit comments

Comments
 (0)