-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support watching independent files #20
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gmethvin Let me know what you think of this approach.
|
||
// If a change in a directory of a non-recursive directory happens, ignore it | ||
if (Files.isDirectory(childPath, NOFOLLOW_LINKS)) { | ||
Path childParentPath = childPath.getParent(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it safe to assume here that if I do mkdirs foo/bar/baz
and foo
is a non-recursive directory, I'll get the event about the creation of foo/bar
before the event about foo/bar/baz
? Otherwise this implementation is flawed.
WatchService watchService = FileSystems.getDefault().newWatchService(); | ||
|
||
int waitInMs = 500; | ||
FileSystem fileSystem = new FileSystem(directoryPath) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's see if Travis's Xcode instance likes this test, but I don't know why it doesn't seem to pass in my Mac OS High Sierra.
43cf8bf
to
88e1ee5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I completely changed the approach and added better tests, CI is passing now. I have one question left though.
Path childPath = eventPath == null ? null : keyRoots.get(key).resolve(eventPath); | ||
logger.debug("{} [{}]", kind, childPath); | ||
if (shouldIgnoreEvent(childPath)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand why we need this. My assumption is that register(_, false)
will register a directory in a non-recursive way. Is that not the case? If it isn't, can we disable recursive file watching at all for these directories in particular so that we don't need to pollute the logic of the watch
method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
register(_, false)
technically should watch non-recursively, but currently the macOS WatchService implementation always watches recursively regardless of what modifier is passed. That's because originally the goal of this library was only to watch directories recursively. One solution is to add this same filtering logic to the MacOSXListeningWatchService
, since that's the only place it should be needed.
Path childPath = eventPath == null ? null : keyRoots.get(key).resolve(eventPath); | ||
logger.debug("{} [{}]", kind, childPath); | ||
if (shouldIgnoreEvent(childPath)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
register(_, false)
technically should watch non-recursively, but currently the macOS WatchService implementation always watches recursively regardless of what modifier is passed. That's because originally the goal of this library was only to watch directories recursively. One solution is to add this same filtering logic to the MacOSXListeningWatchService
, since that's the only place it should be needed.
@@ -105,7 +136,7 @@ public DirectoryWatcher build() throws IOException { | |||
if (logger == null) { | |||
staticLogger(); | |||
} | |||
return new DirectoryWatcher(paths, listener, watchService, enableFileHashing, logger); | |||
return new DirectoryWatcher(paths, files, listener, watchService, enableFileHashing, logger); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we just support regular files in paths
? Is the goal just to make the API more explicit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think having an explicit API helps reasoning about the overhead of watching independent files.
My main motivation to make a distinction between files and directories is that the user of this API needs to split the paths to watch in directories and files. To do that, the files/dirs need to exist, and therefore by construction the user filters out non existing directories (in OSX, I have to do this in bloop because the watcher will fail with an exception if a non-existing directory is passed -- the check is in WatchablePath
IIRC).
@@ -1 +1 @@ | |||
sbt.version=1.0.2 | |||
sbt.version=1.2.1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
d035b0a
to
0129ee0
Compare
Alright, CI has finally passed on OSX with the pertinent changes in its watch service. To avoid the default recursive file watching on OSX, I wanted to use So, we'll live with the current approach. This makes #22 more important for the case of watching independent files efficiently. |
0129ee0
to
efbe696
Compare
@gmethvin There seems to be an error in the Linux CI related to the sbt-sonatype upgrade:
|
efbe696
to
f584fcb
Compare
It seems the resolution error disappeared in the last commit. CI is green again here and PR is ready for review. |
As explained in the docs, JDK's watch service cannot watch independent files and instead can only watch directories. To simulate watching independent files, we watch their parent directories in a non-recursive manner. This PR implements the bits to simulate this behaviour. We only register non-recursive paths if they have not been registered before or if they are not contained in a path that is already been watched.
594e377
to
4c6a9d0
Compare
The zinc implementation had some misbehaviours after the merge commits with the pipelined implementation + changes upstream in Zinc 1.x. This commit fixes those misbehaviours, keeps us updated with the latest Zinc 1.x implementation and improves the compilation task tests. Aside from that, we migrate to directory-watcher too to make sure we have a fast and reliable independent file watching, using the changes proposed in gmethvin/directory-watcher#20
@gmethvin How does this look? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about determining whether the watch path is recursive in the OSX service based on whether the FILE_TREE
modifier is passed, rather than having an extra isRecursive
on the WatchablePath
? Seems like that would be more in line with the "standard" implementation and would make it easier to use the service on its own outside the context of directory-watcher.
/** | ||
* Set multiple files to watch. | ||
* | ||
* Note that the watch services interface does not have the power to watch independent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would say "Note that the JDK WatchService
interface..."
* | ||
* @param files Paths to files (they cannot be directories). | ||
*/ | ||
public Builder files(List<Path> files) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe independentFiles
or regularFiles
? Also let's explicitly mention directories in the documentation for paths
and path
, e.g. "Set a single directory to watch"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we change paths
to directories
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that's probably a good idea, to make it clear it should only be directories.
this.logger = logger; | ||
|
||
/* Register all recursive paths after the non recursive paths to invalidate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused by the comment. We are registering the files
after the recursive paths
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is out of date.
Path parentPath = directory.getParent(); | ||
for (Path nonRecursivePath : nonRecursivePathRoots) { | ||
if (parentPath.equals(nonRecursivePath) && directory.startsWith(nonRecursivePath)) { | ||
ignoreEvent = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason why you used a variable as opposed to return true
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, I can do return true
here instead
private void registerRootsForFiles(List<Path> files) throws IOException { | ||
for (Path file: files) { | ||
Path parentFile = file.getParent(); | ||
if (!watchingPathRoots.contains(parentFile)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
weird indentation (I should probably set up a Java formatter so we don't have to worry about this)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I missed this. A java formatter or an style we can use inside IntelliJ would be ideal.
Do you mean adding I'll act on the rest of your feedback soon. |
No, it should be on a path level, but we can simply check for the
I like this approach because it brings our implementation more in line with the default JDK implementation. It also makes it easier to support additional modifiers in the future if we choose to. |
Makes sense, I'll have a look later this week. |
@jvican I'm going to close this for now since it's been inactive for a while and the changes are out of date, but feel free to pick it up again later if you're still interested. |
Sounds good, thanks Greg. |
As explained in the docs, JDK's watch service cannot watch independent
files and instead can only watch directories. To simulate watching
independent files, we watch their parent directories in a non-recursive
manner. This PR implements the bits to simulate this behaviour.
The strategy here is to first register all the paths that are
non-recursive, and then proceed to the recursive ones. If any of the
directories that must be watched non-recursively are subsumed by any of the
recursive directories, they automatically become recursive.
All events about directories contained in non-recursive directories are
ignored by default.