From 66d631d5a328b8428ef5f964a632ad2ca1a9c634 Mon Sep 17 00:00:00 2001 From: andreatp Date: Fri, 30 Oct 2026 16:12:39 +0000 Subject: [PATCH] fix: signal the watch key when its directory goes away A thread blocked in WatchService.take() is now woken up. Port of google/jimfs@b014510. --- .../roastedroot/zerofs/PollingWatchService.java | 3 ++- .../zerofs/PollingWatchServiceTest.java | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/io/roastedroot/zerofs/PollingWatchService.java b/core/src/main/java/io/roastedroot/zerofs/PollingWatchService.java index 1801b3a0e..3a19f974a 100644 --- a/core/src/main/java/io/roastedroot/zerofs/PollingWatchService.java +++ b/core/src/main/java/io/roastedroot/zerofs/PollingWatchService.java @@ -183,8 +183,9 @@ public void run() { } } catch (IOException e) { // snapshot failed; assume file does not exist or isn't a directory - // and cancel the key + // and cancel the key, then signal it so waiters see it's invalid key.cancel(); + key.signal(); } } } diff --git a/core/src/test/java/io/roastedroot/zerofs/PollingWatchServiceTest.java b/core/src/test/java/io/roastedroot/zerofs/PollingWatchServiceTest.java index d32ff2101..2691efd42 100644 --- a/core/src/test/java/io/roastedroot/zerofs/PollingWatchServiceTest.java +++ b/core/src/test/java/io/roastedroot/zerofs/PollingWatchServiceTest.java @@ -4,8 +4,10 @@ import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -204,6 +206,21 @@ public void testWatchForMultipleEventTypes() throws IOException, InterruptedExce new AbstractWatchService.Event<>(ENTRY_DELETE, 1, fs.getPath("foo")))); } + @Test + public void testDeletingWatchedDirectoryCancelsAndSignalsKey() throws Exception { + ZeroFsPath path = createDirectory(); + AbstractWatchService.Key key = watcher.register(path, List.of(ENTRY_CREATE, ENTRY_DELETE)); + + Files.delete(path); + + WatchKey signalled = watcher.poll(1, SECONDS); + assertSame(key, signalled); + assertFalse(key.isValid()); + assertFalse(key.reset()); + assertFalse(watcher.isPolling()); + assertTrue(key.pollEvents().isEmpty()); + } + private void assertWatcherHasEvents(WatchEvent... events) throws InterruptedException { assertWatcherHasEvents(Arrays.asList(events), List.>of()); }