提交 0d711a89 编写于 作者: A alanb

7164570: (fs) WatchService queues CREATE event but not DELETE event for very...

7164570: (fs) WatchService queues CREATE event but not DELETE event for very short lived files [sol11]
Reviewed-by: chegar
上级 306eaf31
......@@ -314,7 +314,7 @@ class SolarisWatchService
fileKey2WatchKey.put(fileKey, watchKey);
// register all entries in directory
registerChildren(dir, watchKey, false);
registerChildren(dir, watchKey, false, false);
return watchKey;
}
......@@ -486,7 +486,8 @@ class SolarisWatchService
void processDirectoryEvents(SolarisWatchKey key, int mask) {
if ((mask & (FILE_MODIFIED | FILE_ATTRIB)) != 0) {
registerChildren(key.getDirectory(), key,
key.events().contains(StandardWatchEventKinds.ENTRY_CREATE));
key.events().contains(StandardWatchEventKinds.ENTRY_CREATE),
key.events().contains(StandardWatchEventKinds.ENTRY_DELETE));
}
}
......@@ -535,14 +536,16 @@ class SolarisWatchService
/**
* Registers all entries in the given directory
*
* The {@code sendEvents} parameter indicates if ENTRY_CREATE events
* should be queued when new entries are found. When initially
* registering a directory then will always be false. When re-scanning
* a directory then it depends on if the event is enabled or not.
* The {@code sendCreateEvents} and {@code sendDeleteEvents} parameters
* indicates if ENTRY_CREATE and ENTRY_DELETE events should be queued
* when new entries are found. When initially registering a directory
* they will always be false. When re-scanning a directory then it
* depends on if the events are enabled or not.
*/
void registerChildren(UnixPath dir,
SolarisWatchKey parent,
boolean sendEvents)
boolean sendCreateEvents,
boolean sendDeleteEvents)
{
// if the ENTRY_MODIFY event is not enabled then we don't need
// modification events for entries in the directory
......@@ -550,14 +553,7 @@ class SolarisWatchService
if (parent.events().contains(StandardWatchEventKinds.ENTRY_MODIFY))
events |= (FILE_MODIFIED | FILE_ATTRIB);
DirectoryStream<Path> stream = null;
try {
stream = Files.newDirectoryStream(dir);
} catch (IOException x) {
// nothing we can do
return;
}
try {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
for (Path entry: stream) {
Path name = entry.getFileName();
......@@ -565,32 +561,34 @@ class SolarisWatchService
if (parent.getChild(name) != null)
continue;
// send ENTRY_CREATE if enabled
if (sendEvents) {
parent.signalEvent(StandardWatchEventKinds.ENTRY_CREATE, name);
}
// register it
// attempt to register entry
long object = 0L;
int errno = 0;
try {
object = registerImpl((UnixPath)entry, events);
} catch (UnixException x) {
// can't register so ignore for now.
continue;
errno = x.errno();
}
boolean registered = (object != 0L);
boolean deleted = (errno == ENOENT);
if (registered) {
// create node
EntryNode node = new EntryNode(object, entry.getFileName(), parent);
// tell the parent about it
parent.addChild(entry.getFileName(), node);
object2Node.put(object, node);
}
// create node
EntryNode node = new EntryNode(object, entry.getFileName(), parent);
// tell the parent about it
parent.addChild(entry.getFileName(), node);
object2Node.put(object, node);
if (sendCreateEvents && (registered || deleted))
parent.signalEvent(StandardWatchEventKinds.ENTRY_CREATE, name);
if (sendDeleteEvents && deleted)
parent.signalEvent(StandardWatchEventKinds.ENTRY_DELETE, name);
}
} catch (ConcurrentModificationException x) {
// error during iteration which we ignore for now
} finally {
try {
stream.close();
} catch (IOException x) { }
} catch (DirectoryIteratorException | IOException x) {
// nothing we can do
}
}
......
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 7164570
* @summary Test that CREATE and DELETE events are paired for very
* short lived files
* @library ..
* @run main MayFlies
*/
import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.*;
import java.util.*;
import java.util.concurrent.*;
public class MayFlies {
static volatile boolean stopped;
static volatile boolean failure;
/**
* Continuously creates short-lived files in a directory until {@code
* stopped} is set to {@code true}.
*/
static class MayFlyHatcher implements Runnable {
static final Random rand = new Random();
private final Path dir;
private final String prefix;
private MayFlyHatcher(Path dir, String prefix) {
this.dir = dir;
this.prefix = prefix;
}
static void start(Path dir, String prefix) {
MayFlyHatcher hatcher = new MayFlyHatcher(dir, prefix);
new Thread(hatcher).start();
}
public void run() {
try {
int n = 0;
while (!stopped) {
Path name = dir.resolve(prefix + (++n));
Files.createFile(name);
if (rand.nextBoolean())
Thread.sleep(rand.nextInt(500));
Files.delete(name);
Thread.sleep(rand.nextInt(100));
}
System.out.format("%d %ss hatched%n", n, prefix);
} catch (Exception x) {
failure = true;
x.printStackTrace();
}
}
}
/**
* Test phases.
*/
static enum Phase {
/**
* Short-lived files are being created
*/
RUNNING,
/**
* Draining the final events
*/
FINISHING,
/**
* No more events or overflow detected
*/
FINISHED
};
public static void main(String[] args) throws Exception {
// schedules file creation to stop after 10 seconds
ScheduledExecutorService pool = Executors.newSingleThreadScheduledExecutor();
pool.schedule(
new Runnable() { public void run() { stopped = true; }},
10, TimeUnit.SECONDS);
Path dir = TestUtil.createTemporaryDirectory();
Set<Path> entries = new HashSet<>();
int nCreateEvents = 0;
boolean overflow = false;
try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE);
// start hatching Mayflies
MayFlyHatcher.start(dir, "clinger");
MayFlyHatcher.start(dir, "crawler");
MayFlyHatcher.start(dir, "burrower");
MayFlyHatcher.start(dir, "swimmer");
Phase phase = Phase.RUNNING;
while (phase != Phase.FINISHED) {
// during the running phase then poll for 1 second.
// once the file creation has stopped then move to the finishing
// phase where we do a long poll to ensure that all events have
// been read.
int time = (phase == Phase.RUNNING) ? 1 : 15;
key = watcher.poll(time, TimeUnit.SECONDS);
if (key == null) {
if (phase == Phase.RUNNING && stopped)
phase = Phase.FINISHING;
else if (phase == Phase.FINISHING)
phase = Phase.FINISHED;
} else {
// process events
for (WatchEvent<?> event: key.pollEvents()) {
if (event.kind() == ENTRY_CREATE) {
Path name = (Path)event.context();
boolean added = entries.add(name);
if (!added)
throw new RuntimeException("Duplicate ENTRY_CREATE event");
nCreateEvents++;
} else if (event.kind() == ENTRY_DELETE) {
Path name = (Path)event.context();
boolean removed = entries.remove(name);
if (!removed)
throw new RuntimeException("ENTRY_DELETE event without ENTRY_CREATE event");
} else if (event.kind() == OVERFLOW) {
overflow = true;
phase = Phase.FINISHED;
} else {
throw new RuntimeException("Unexpected event: " + event.kind());
}
}
key.reset();
}
}
System.out.format("%d ENTRY_CREATE events read%n", nCreateEvents);
// there should be a DELETE event for each CREATE event and so the
// entries set should be empty.
if (!overflow && !entries.isEmpty())
throw new RuntimeException("Missed " + entries.size() + " DELETE event(s)");
} finally {
try {
TestUtil.removeAll(dir);
} finally {
pool.shutdown();
}
}
if (failure)
throw new RuntimeException("Test failed - see log file for details");
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册