-
Notifications
You must be signed in to change notification settings - Fork 29.6k
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
fs.watchFile fixes #2093
fs.watchFile fixes #2093
Conversation
// Test ENOENT. Should fire once. | ||
var enoentFile = path.join(fixtures, 'empty', 'non-existent-file'); | ||
fs.watchFile(enoentFile, function(curr, prev) { | ||
callbackFired++; |
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.
Wouldn't it be better if we had the condition to check if the callback is really for ENOENT
, as suggested here #2028 (comment)?
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'd think it'd be better too, but I'm not sure how to ensure it is ENOENT
. Is checking curr.ino > 0
the only way/right way?
edit: cause I don't know what ino
stands for :/
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.
@brendanashworth I think it is inode number. cc @bnoordhuis
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, it's the inode number. curr.ino > 0
is just an example, though; on error, all the fields will be zero. It would be good to document that behavior in the fs.watchFile()
documentation.
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.
@bnoordhuis I was thinking, once this lands, I ll include the checks for non-existent file in fs.Stats
section and mention that in fs.watchFile
section. That should be fine, right?
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 think fs.Stats
is the right place for that, it's specific to fs.watchFile()
.
Had only minor comments. It can land as-is, I guess. |
Why would you change the behavior on non-existant files? It doesn't make much sense if the callback is triggered immediately, without any actual changes, as I reported in #1745. |
@kanongil See this comment. Executive summary: no callback means you can't distinguish between no file and no events. |
@bnoordhuis Ah, I see. I guess this makes sense. |
When the listener was truthy but NOT a function, fs.watchFile would throw an error through the EventEmitter. This caused a problem because it would only be thrown after the listener was started, which left the listener on. There should be no backwards compatability issues because the error was always thrown, just in a different manner. Also adds tests for this and other basic functionality.
4d5655d
to
d9c795e
Compare
Pushed up fixed commits, PTAL @thefourtheye @bnoordhuis. |
fs.watchFile(enoentFile, function(curr, prev) { | ||
callbackFired++; | ||
|
||
fs.unwatchFile(enoentFile); |
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 am not sure if unwatching here is a good idea. If some change triggers the callback more than once, even in the ENOENT
case, then this testcase will miss that, as it is unwatching immediately in the first invocation itself. @bnoordhuis What do you think?
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's the alternative?
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 am not sure if it will work fine but how about unwatching at the process's exit
event?
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.
It won't exit without unwatching, as the watcher will keep the process open.
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.
Oh, then I am out of ideas. We can leave it as it is I guess. Thanks for clarifying @brendanashworth :-)
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.
@trevnorris Can you please explain the making it not persistent part? Actually the original PR had a setTimeout of 2ms and unwatch was called from there. You meant something like that?
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.
Reference documentation: https://iojs.org/api/fs.html#fs_fs_watchfile_filename_options_listener
persistent
indicates whether the process should continue to run as long as files are being watched
I'm assuming that a file that doesn't exist can't be watched. Thus allowing the process to shutdown.
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 think that would work. { persistent: false }
unrefs the handle immediately. io.js would quit as soon as the script finished, before the callback has a chance to run.
FWIW, the test looks fine to me.
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.
That's what I was wondering.
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.
When I had a timer in, the test was admittedly very flaky (10-20% failure rate).
LGTM FWIW. |
@thefourtheye LGTY? |
@brendanashworth I was okay with the initial version itself ;-) LGTM. |
LGTM |
|
||
// Test ENOENT. Should fire once. | ||
const enoentFile = path.join(fixtures, 'empty', 'non-existent-file'); | ||
fs.watchFile(enoentFile, function(curr, prev) { |
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.
Can we simply use common.mustCall
here and get rid of the counter?
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 had the counter in so that if the callback were fired more than once, it would error. I'm not sure if that is still possible unless it is called synchronously twice - but just in case, I'd like to err on the side of keeping the counter.
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.
common.mustCall()
checks the call count at exit. You don't strictly need a counter 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.
I will change this.
I plan on merging tonight. edit: nope nevermind |
When fs.watchFile encounters an ENOENT error, it invokes the given callback with some error data. This caused an issue as it was different behaviour than Node v0.10. Instead of changing this behaviour, document it and add a test. Ref: nodejs#1745 Ref: nodejs#2028
d9c795e
to
f6467b0
Compare
Force pushed with |
// Test ENOENT. Should fire once. | ||
const enoentFile = path.join(fixtures, 'empty', 'non-existent-file'); | ||
fs.watchFile(enoentFile, common.mustCall(function(curr, prev) { | ||
fs.unwatchFile(enoentFile); |
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.
Oh, we are not doing inode check to confirm to see if the file really doesn't exist. Meh. Its okay I guess. LGTM 👍
One more LGTM, with a documentation suggestion. Can you run the CI before landing? |
CI is happy: https://jenkins-iojs.nodesource.com/job/iojs+any-pr+multi/141/. @thefourtheye would you like to submit a follow up PR with the doc addition? I don't know enough to make a good writeup. |
@brendanashworth I think its high time you landed this ;-) We can discuss on this thread about the documentation even after that :-) |
When the listener was truthy but NOT a function, fs.watchFile would throw an error through the EventEmitter. This caused a problem because it would only be thrown after the listener was started, which left the listener on. There should be no backwards compatability issues because the error was always thrown, just in a different manner. Also adds tests for this and other basic functionality. PR-URL: #2093 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
When fs.watchFile encounters an ENOENT error, it invokes the given callback with some error data. This caused an issue as it was different behaviour than Node v0.10. Instead of changing this behaviour, document it and add a test. Ref: #1745 Ref: #2028 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> PR-URL: #2093
Thank you reviewers, sorry for the lag. Merged in 12bc397...23efb05. |
@brendanashworth Awesome :-) Thanks :-) |
} | ||
|
||
if (!listener) { | ||
if (typeof listener !== 'function') { | ||
throw new Error('watchFile requires a listener function'); |
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.
A bit late, since already merged, but shouldn't this be a TypeError
?
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.
Never too late :) you're right though, if you wanted to submit a PR I'd review it.
As per the discussion in nodejs#2093 (comment), this patch improves the Note section in watchFile to clarify about the non-existent files. This patch also includes a test which checks if a file not present, the callback is invoked at least once and if the file is created after the callback is invoked, it will be invoked again with new stat objects. PR-URL: nodejs#2169
As per the discussion in #2093 (comment), this patch documents the behavior of calling fs.watchFile() with a path that does not yet exist. This patch also includes a test which checks if a file not present, the callback is invoked at least once and if the file is created after the callback is invoked, it will be invoked again with new stat objects. PR-URL: #2169 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Brendan Ashworth <brendan.ashworth@me.com>
There were some issues with
fs.watchFile
:This fixes them. Supersedes #2028, fixes #1745.
/cc @thefourtheye @bnoordhuis @evanlucas