-
Notifications
You must be signed in to change notification settings - Fork 247
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
WIP: Use NIO.2 WatchService for JRuby #463
Conversation
4481b84
to
30fd05a
Compare
Hmm. The acceptance specs don't seem to be passing on the JRuby builds on Travis CI. 😞 For some reason, I can only get them to pass if I set the OS to OSX. |
b51bdda
to
ba5fb2d
Compare
lib/listen/adapter/jruby.rb
Outdated
child = dir.resolve(name) | ||
pathname = Pathname.new(child.to_s).dirname | ||
_queue_change(:dir, pathname, '.', recursive: true) | ||
valid = key.reset |
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.
Not a ruby dev, but:
- Should reset be called after this loop, not in it.
- Should reset be called regardless of the result of dir.nil?
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 reset be called after this loop, not in it.
You're quite right - I got lost in all the braces in the Java example. I've addressed this in a fix-up commit.
- Should reset be called regardless of the result of dir.nil?
It doesn't appear to be the case in the Java example, but what you say makes sense. I think in the Java example, they're probably assuming it can never happen, because every path
that's registered will have an entry in the keys
Map
. The same thing is probably true of the current implementation, but I can see a case for doing as you suggest, so I've made the change in another fix-up commit.
Hmm. I just read this at the end of the documentation:
Maybe that's what I'm seeing on Mac OSX. |
Wow, cool that this got a quick impl so...quickly! The lag times could be influenced by slow JRuby startup? Also per-platform support...yes I think one thing we'll want to look into is whether there's a way to query whether it's going to use filesystem events or polling. In the cases where the JDK is using polling, we can explore other options like using FFI to call into MacOS inotify APIs. I will have a look at this tonight or tomorrow. Thanks @floehopper! |
Ok so it looks like only BSD/MacOS do not have support for using a native filesystem eventing system. Windows uses ReadDirectoryAttributes, Linux uses inotify, Solaris uses something similar. I think the simplest way to detect this would be to get the WatchService instance and then check if it's the polling one. Unfortunately there's no public API for this, but the following kinda-gross hack should work:
|
I found a MacOS FSE library for JDK that uses JNA, a library similar to the JNR we use to implement FFI. This code could probably be translated into Ruby FFI pretty easily: |
I'm pretty sure this wasn't the issue I was seeing on my Mac OSX development machine. Running the following script and executing require 'java'
java_import 'java.nio.file.FileSystems'
java_import 'java.nio.file.Paths'
java_import 'java.nio.file.StandardWatchEventKinds'
require 'pathname'
require 'fileutils'
EVENT_KINDS = [
StandardWatchEventKinds::ENTRY_CREATE,
StandardWatchEventKinds::ENTRY_MODIFY,
StandardWatchEventKinds::ENTRY_DELETE
]
directory = Pathname.pwd
watcher = FileSystems.getDefault.newWatchService
path = Paths.get(directory.to_s)
keys = {}
key = path.register(watcher, *EVENT_KINDS)
keys[key] = path
puts 'Monitoring started...'
loop do
key = watcher.take
dir = keys[key]
unless dir.nil?
key.pollEvents.each do |event|
kind = event.kind
next if kind == StandardWatchEventKinds::OVERFLOW
name = event.context
child = dir.resolve(name)
lag = Time.now - File.mtime(child.to_s)
p [child.to_s, lag]
end
end
valid = key.reset
unless valid
keys.delete(key)
break if keys.empty?
end
end
My development machine is running MacOs, so I guess this explains the results above - presumably in this case it has fallen back to polling...? This seems to be confirmed by adding the following line to the script above: puts watcher.class.name # => "Java::SunNioFs::PollingWatchService" |
I've also just noticed that the JRuby builds are failing even on |
1180af0
to
6fb459e
Compare
cf56792
to
5acaaeb
Compare
Doh! The Linux adapter was being picked up in preference to the JRuby adapter. I've moved the JRuby adapter to the beginning of the |
lib/listen/adapter/jruby.rb
Outdated
next if kind == StandardWatchEventKinds::OVERFLOW | ||
name = event.context | ||
child = dir.resolve(name) | ||
p child.to_s |
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.
Layout/IndentationConsistency: Inconsistent indentation detected.
bc02bad
to
c63ed31
Compare
c63ed31
to
27c157a
Compare
end | ||
|
||
def _run | ||
loop do |
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.
Metrics/BlockLength: Block has too many lines. [26/25]
I've realised I don't yet fully understand how the gem is architected and so I think I have quite a bit of work to do before this will be ready to merge, so I'm going to close the PR, but continue working on the branch. |
This is a quick & hacky attempt at getting the basics of #462 working closely based on the WatchDir example mentioned in the documentation.
I found that I had to bump
LISTEN_TESTS_DEFAULT_LAG
up when running the acceptance specs on my development machine (MacOS v10.13.6):Although I didn't try to minimise the value of
LISTEN_TESTS_DEFAULT_LAG
, I was using ~5 seconds to get the acceptance specs to pass successfully. This doesn't seem very encouraging, but maybe I'm doing something wrong! I'm hoping that pushing this up will yield some better results from Travis CI.To-do (not exhaustive)
callback
passed into#_configure
as intended...?#_process_event
as intended...?Listen::Adapter::Jruby.usable?
implementation is sensible/cc @headius @bratish