-
Notifications
You must be signed in to change notification settings - Fork 7.3k
fs: Add file descriptor support to *File() funcs #8522
Conversation
as requested by @rlidwka and @chrisdickinson in #8471 |
|
||
If the `encoding` option is specified then this function returns a | ||
string. Otherwise it returns a buffer. |
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.
Why remove these two lines?
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 similar note is part of the fs.readFile(file[, options], callback)
documentation, which is referenced by fs.readFileSync(file[, options])
. No other synchronous method has any additional notes aside from referencing the asynchronous version. I tried to make the documentation more uniform, despite the note being accurate. Less duplication, less potential for errors.
Would you prefer me putting it back in?
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.
Go ahead and leave it in. Most other functions *Sync()
functions can refer the user to a man
page. This is a special case sense the return value is not always the same (i.e. Buffer or String).
Thanks for the review, @trevnorris! I made the proposed changes and updated the branch. Let me know if there is anything else you would like changed. |
@trevnorris I am not sure whether you got notified (sorry to disturb if you are just busy). Is there anything else I can do to improve this? |
@jwueller No worries about bugging me. It's helpful to be pinged. :) So far everything is looking good. /cc @indutny @tjfontaine Either of you have a problem with this change? |
Since @trevnorris said it's helpful, I'll shamelessly ping again ;) @indutny @tjfontaine |
@@ -113,6 +113,10 @@ function nullCheck(path, callback) { | |||
return true; | |||
} | |||
|
|||
function isFd(path) { | |||
return (path >>> 0) === path && path > 0; |
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 just typeof(path) === 'number' && fd >= 0
? Zero is a valid file descriptor.
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 allows path
to be NaN
. The point of the uint32
conversion check is that detects that, and is less prone to deoptimization than typeof
.
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.
Okay, but the point about zero being a valid file descriptor still stands. :-)
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.
True.
@jwueller Please change it to (path >>> 0) === path && path >= 0
.
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 already did that and the other suggestions locally. It will get pushed as soon as I am back at my workstation (probably Monday).
@bnoordhuis @trevnorris There you go; finally got around to pushing my changes. |
} else { | ||
fs.close(fd, function() { | ||
callback(er); | ||
}); |
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.
Potential problem is that the fs.close()
itself could error. Not sure what the best solution would be in that case, but seems like a potential fd leak if we ignore any errors coming from the fs.close()
cb.
@bnoordhuis Thoughts on how to deal w/ 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.
@trevnorris Can one even recover from a file failing to close? It appears that the state of a file descriptor is unspecified if it fails:
If close() is interrupted by a signal that is to be caught, it shall return -1 with errno set to [EINTR] and the state of fildes is unspecified. If an I/O error occurred while reading from or writing to the file system during close(), it may return -1 with errno set to [EIO]; if this error is returned, the state of fildes is unspecified.
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.
Assuming fs.close() calls close(2), it cannot fail to release the fd. The error returns from close are purely advisory. Linux documents only EBADF (which isn't a leak, obviously), EIO (which is a delayed error from a prev i/o operation, interesting, but not a leak, and only NFS, I think), and EINTR, of which the following note applies:
Note that the return value should only be
used for diagnostics. In particular close() should not be retried after an EINTR since this may
cause a reused descriptor from another thread to be closed.
- man 2 close
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.
@sam-github According to the fs
documentation, we are dealing with close(2)
. I haven't looked at the actual code, though.
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.
Hm, looks like the open group might've had to align to solaris: https://docs.oracle.com/cd/E23824_01/html/821-1463/close-2.html#scrolltoc which allows the fd to be left in an unspecified condition. Wtf? Anyhow, no recover possible, indeed.
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.
Wow. WTF are you supposed to do w/ a fd in an "unspecified condition"?
Either way, should we report this to the user, and if so then how?
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.
Reporting it would probably not be very helpful, since we are already in an error state at this point. The first failure is likely more interesting to the user, as he might be able to do something about it. There is nothing a user can do about close(2)
failing. The second error may even be a subsequent failure of the first. I propose we follow the usual paradigm and report the "topmost" error to the user.
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 second @jwueller's suggestion. It's what libuv does.
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.
Sounds good.
@jwueller Looking good. Few more comments. |
@trevnorris branch updated, one remaining question |
@@ -527,7 +535,10 @@ Example: | |||
console.log('The "data to append" was appended to file!'); | |||
}); | |||
|
|||
## fs.appendFileSync(filename, data, [options]) | |||
Note that any specified file descriptor needs to be openend for appending. |
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.
Typo: openend
should be opened
.
@jwueller @trevnorris What do we need to do to move this PR forward? |
@misterdjules probably a rebase. Last I looked it was fine. Once it's rebased I'll take another glance and sign off. |
These changes affect the following functions and their synchronous counterparts: * fs.readFile() * fs.writeFile() * fs.appendFile() If the first parameter is a uint >= 0, it is treated as a file descriptor. In all other cases, the original implementation is used to ensure backwards compatibility. File descriptor ownership is never taken from the user. The documentation was adjusted to reflect these API changes. A note was added to make the user aware of file descriptor ownership and the conditions under which a file descriptor can be used by each of these functions. Tests were extended to test for file descriptor parameters under the conditions noted in the relevant documentation.
@trevnorris Oh my, I just noticed that this PR is still open and pushed a rebase. Do we need anything else? |
@trevnorris @misterdjules Should I add a test for |
@jwueller ... well, thank you for being so patient on this one. At this point, given the API change, this is something that would need to be closed here then re-targeted at master with a new PR on http://github.com/nodejs/node. |
@jasnell I'll do that, thank you for the update. |
These changes affect the following functions and their synchronous counterparts:
fs.readFile()
fs.writeFile()
fs.appendFile()
If the first parameter is a
uint >= 0
, it is treated as a file descriptor. In all other cases, the original implementation is used to ensure backwards compatibility. File descriptor ownership is never taken from the user.The documentation was adjusted to reflect these API changes. A note was added to make the user aware of file descriptor ownership and the conditions under which a file descriptor can be used by each of these functions.
Tests were extended to test for file descriptor parameters under the conditions noted in the relevant documentation.