-
Notifications
You must be signed in to change notification settings - Fork 3.8k
unix: allow nbufs>IOV_MAX in uv_fs_read and uv_fs_write (replaces #269) #448
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
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.
Set req->bufs to NULL.
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.
Ah, so that's what Ben meant :)
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.
Done
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.
You have undone 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.
I messed up the rebase, it's back now.
|
Can you also add a test which uses an offset other than -1? |
|
Hi all, sorry for the late response, I indeed think this PR should have landed some time ago. Regarding my own pull request I have the following remarks: For the first issue I propose a publicly available uv__getiovmax defined as: Swapping the _SC_IOV_MAX and IOV_MAX ifdefs With this solution any users needing to send more than getiovmax() buffers could do this in a loop. To resolve the second issue, I think my solution should be refactored into a glibc/writev solution:
I am willing to provide a solution for this, but my time is really limited, so if anyone wants to beat me to it, you're most welcome! |
|
hey @bwijen glad to see you return to this work! this and/or your original PR is really important, whichever one eventually lands! 👍 |
|
@bwijen Welcome back, and thanks for your feedback. Is this joined feedback from @bnoordhuis and yourself based on your e-mail correspondence? I understand that leaving it to the user to loop may seem advantageous, although in the case of Node.js I think it will make no difference at all (as far as I can see, it won't be able to leverage its potential advantage given the current state of writable streams.. but I may be wrong and input from someone like @bnoordhuis would be most appreciated). As for the suggested implementation detail of moving the loop to Node-land, why no doubt possible, it would be a massive pain to do it from C++, because of the asynchronous chaining it would cause (multiple req objects that need to be sequentially executed one after the other). So the only alternative really becomes to do this from JavaScript. Since the current implementation is 100% in C++ land, this will most certainly cost us in performance, as well as add complexity (unless someone else sees another way out?). I do assume that one of the design goals of libuv is to take care of some level of complexity, so its users don't suffer that complexity. You mention "To resolve the second issue". Are you talking about atomicity? Your suggestion sounds like a very painful one (in terms of memory used and copied around). I'm also not sure if you're suggesting to actually land this in glibc (I doubt that's realistic, but that's how it reads). In any case. How about a best-of-both-worlds approach? Expose IOV_MAX to user-land, so they have the freedom to use its advice, but they may choose to ignore it and leave it to libuv to write all the available buffers in one single C++ loop (as your PR does). |
I don't really like this so much. What's the real benefit? We already have a precedent of doing it internally (see streams), so I don't see why we shouldn't do the same for fs operations.
There shouldn't be a need for that. The contract is that as long as the completion callback hasn't been called, libuv holds on to the buffers. We should never copy the content of those buffers within libuv. |
|
Quick note: "this or that makes things easier for Node" is not a good justification, this is libuv, separate project. If there is something we need to change / expose, we will, but it needs merits of its own. |
|
Unless there are issues I haven't seen, this PR sounds like the right thing to do, as we already do for uv_stream_t. |
|
I agree that Node is not the goal. However, it can be used to present real life use cases (just like Node is a real life use case for V8, and it hurts when the V8 team makes breaking changes that have a lot of collateral cost for the Node team). In any case, it seems you (@saghul) do think this PR is the right approach, so we are on the same page. I think it's libuv's job (node or no node) to present a friendly interface to the user, and hide the complicated parts and OS-specific limitations of doing I/O. And indeed, it's not like streams use a different approach. I will move forward by addressing the issues exposed in this PR by @saghul. If in the future anyone decides that this PR is philosophically incorrect, I encourage improvement, but there's imho nothing wrong with iteration. I have to admit that I'm new to libuv, so I'm not really familiar with the who's-who and who inevitably is likely to decide on whether to merge this (which right now makes me feel a bit like between a rock and hard place when receiving contradicting feedback). |
My note was just a hint not to derail the discussion, of course I care about not breaking Node :-)
We encourage open collaboration, so everyone is more than welcome to contribute to the discussion. As for merging the actual PR or making final design / API decisions, both @bnoordhuis and I are libuv Core Janitors (there are more, check the contributing guide, but he and I are the ones involved in this discussion at the moment). |
|
Thank you for the feedback 👍 |
|
Thinking about this again (and reading the feedback) I agree that this is indeed the way to go. @ronkorving: So what's next? We have now have both your #448 and my #269 I have no problem with withdrawing mine so we can all focus on yours... |
Lets discuss that in another issue / PR (feel free to open one), it doesn't need to be tied to this one. |
|
@bwijen I have time to work on this and get it into shape. You deserve all the credit for the fix, but I really don't mind taking it to the finish line. It's a great learning experience for me too. But it's your baby and thus your call. All I would ask for is: sooner rather than later :) |
|
@ronkorving I have closed #269, so lets focus on this one as of now... |
|
Understood 👍 |
src/unix/fs.c
Outdated
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.
Feedback on this line would be appreciated. @bwijen's PR had this as if (result <= 0) {. Any feedback on which makes more sense?
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.
The reason I included the zero, is that a read can return zero for a successful read, indicating the end of a stream.
If you do not check for zero, another read will be executed, resulting in a failure (reading past the end of a file, or worse, returning zero again.) The same holds for a write where a zero indicates nothing was written, which you don't want to redo in the iteration context.
|
@saghul speaking of tests.. any idea why |
src/unix/fs.c
Outdated
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.
Do this unconditionally, even if req->bufs is == to req->bufsml.
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.
Got it
|
@ronkorving why should it break? This PR should fix it, shouldn't it? |
|
I mean it doesn't even break on the v1.x branch (I'm on OSX btw). |
|
So, you tested the test without the rest of the patch? No idea, cannot try it right now, sorry. |
|
When I checkout v1.x, the test (that was already in place passes). From the looks of it, it shouldn't pass at all. Maybe the test is broken. |
|
I think you have mixed up your branches. There is no such a test in the v1.x branch. This very PR adds it. |
|
Sigh. It must be Wednesday. I never could get the hang of Wednesdays. |
|
Looking good! Can you please add another test which uses some initial offset please? Thanks! |
|
@ronkorving nope, it succeeds! We don't quite have a green build, but your test is passing AFAIS. Good job! Can you please squash all your commits into one and rebase if necessary? |
|
Oh really? Hurray! About to catch to some sleep, but will do that in about 11 hours. Thanks for everything @saghul! |
00b3bd1 to
cf03f1b
Compare
|
@saghul squashed the lot. |
|
@ronkorving I still see 5 commits. Please make it one and adjust the commit log to our guidelines in CONTRIBUTING.md. Thanks! |
|
Oh, I thought you wanted to keep @bwijen's the way they were, as you said "squash all your commits" (emphasis added by me). I guess I took that a bit too literal. Give me a moment, and I'll try to squash the whole bunch. |
|
That's it, everything in a single commit. |
test/test-fs.c
Outdated
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.
Please don't use camelCase. Use sanake_case. So, that's filler_len.
|
I messed up, give me a moment. |
7c8f5e4 to
9bd3975
Compare
|
All done. |
|
Are we there yet? :) CI looks promising. |
|
We are. LGTM, landing now. |
This allows writing and reading any amount of buffers, regardless of what IOV_MAX may be defined as. It also moves the IOV_MAX test from stream to core. This is based on the excellent work of @bwijen in #269. Refs: #269 PR-URL: #448 Reviewed-By: Saúl Ibarra Corretgé <[email protected]>
|
Landed in ✨ 2bf7827 ✨ Thanks a lot @ronkorving and @bwijen! |
|
\o/ Thanks guys! |
This allows writing and reading any amount of buffers, regardless of what IOV_MAX may be defined as. It also moves the IOV_MAX test from stream to core. This is based on the excellent work of @bwijen in libuv#269. Refs: libuv#269 PR-URL: libuv#448 Reviewed-By: Saúl Ibarra Corretgé <[email protected]>
This allows writing and reading any amount of buffers, regardless of what IOV_MAX may be defined as. It also moves the IOV_MAX test from stream to core. This is based on the excellent work of @bwijen in libuv#269. Refs: libuv#269 PR-URL: libuv#448 Reviewed-By: Saúl Ibarra Corretgé <[email protected]>
Building on the excellent work of @bwijen, I was hoping to move the PR from March forward by addressing the last remaining concerns.
This PR also unblocks it from merging, as there currently is a merge conflict on #269.
Of course if @bwijen has time to review this PR or address the issues in his own PR #269, that would be most appreciated. In any case, he deserves the credit for the contribution. I'm just trying to help get it to the finish line, as I would really like to see it land. It has a big positive performance impact on io.js.
cc @saghul, @bnoordhuis