Skip to content

Conversation

@ronkorving
Copy link
Contributor

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

Copy link
Member

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.

Copy link
Contributor Author

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 :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have undone this.

Copy link
Contributor Author

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.

@saghul
Copy link
Member

saghul commented Jul 20, 2015

Can you also add a test which uses an offset other than -1?

@bwijen
Copy link
Contributor

bwijen commented Jul 20, 2015

Hi all, sorry for the late response, I indeed think this PR should have landed some time ago.
I did have some emailthread going on with @bnoordhuis but this was rather going slow. Anyway I think continuing the discussion here is much more meaningful, so here it goes.

Regarding my own pull request I have the following remarks:
First of all, for my own implementation (the original reason for #260) it is enough to have public access to 'uv__getiovmax()' With a publicly available uv__getiovmax() a user could just fill the right (maximum) amount of buffers and issue a write.
Secondly, POSIX states that writev should be atomic as @ronkorving also already noted.

For the first issue I propose a publicly available uv__getiovmax defined as:

int uv__getiovmax() {
#if defined(_SC_IOV_MAX)
 static int iovmax=-1;
 if(iovmax<0) sysconf(_SC_IOV_MAX);
 return iovmax;
#elif defined(IOV_MAX)
 return IOV_MAX;
#else
 return 1024;
#endif
}

Swapping the _SC_IOV_MAX and IOV_MAX ifdefs
And reintroducing the limits.h include with __need_IOV_MAX

With this solution any users needing to send more than getiovmax() buffers could do this in a loop.
So staying under this limit or choosing to go over 'knowing' it will most certainly hurt performance is up to the users... (@ronkorving: Would this be a viable solution for node.js, meaning node.js will provide the loop to stay under the limit?)

To resolve the second issue, I think my solution should be refactored into a glibc/writev solution:

  • calculate total combined size of all buffers
  • allocate memory
  • copy all buffers to alloc'd memory
  • call write with alloc'd memory

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!

@reqshark
Copy link

hey @bwijen glad to see you return to this work! this and/or your original PR is really important, whichever one eventually lands! 👍

@ronkorving
Copy link
Contributor Author

@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).

@saghul
Copy link
Member

saghul commented Jul 21, 2015

With this solution any users needing to send more than getiovmax() buffers could do this in a loop.

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.

To resolve the second issue, I think my solution should be refactored into a glibc/writev solution:

calculate total combined size of all buffers
allocate memory
copy all buffers to alloc'd memory
call write with alloc'd memory

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.

@saghul
Copy link
Member

saghul commented Jul 21, 2015

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.

@saghul
Copy link
Member

saghul commented Jul 21, 2015

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.

@ronkorving
Copy link
Contributor Author

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).

@saghul
Copy link
Member

saghul commented Jul 21, 2015

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).

My note was just a hint not to derail the discussion, of course I care about not breaking Node :-)

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).

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).

@ronkorving
Copy link
Contributor Author

Thank you for the feedback 👍

@bwijen
Copy link
Contributor

bwijen commented Jul 21, 2015

Thinking about this again (and reading the feedback) I agree that this is indeed the way to go.
I still think my first issue holds, I would really appreciate a public api to uv__getiovmax()
For the second issue I have to admit, that libuv has no need to be atomic. (@bnoordhuis: had already pointed this out, but I wasn't convinced at the time.)

@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...

@saghul
Copy link
Member

saghul commented Jul 21, 2015

I would really appreciate a public api to uv__getiovmax()

Lets discuss that in another issue / PR (feel free to open one), it doesn't need to be tied to this one.

@ronkorving
Copy link
Contributor Author

@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 :)

@bwijen
Copy link
Contributor

bwijen commented Jul 21, 2015

@ronkorving I have closed #269, so lets focus on this one as of now...

@ronkorving
Copy link
Contributor Author

Understood 👍

src/unix/fs.c Outdated
Copy link
Contributor Author

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?

Copy link
Contributor

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.

@ronkorving
Copy link
Contributor Author

@saghul speaking of tests.. any idea why make check passes, while the test fs_write_alotof_bufs seems should break?

src/unix/fs.c Outdated
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it

@saghul
Copy link
Member

saghul commented Jul 22, 2015

@ronkorving why should it break? This PR should fix it, shouldn't it?

@ronkorving
Copy link
Contributor Author

I mean it doesn't even break on the v1.x branch (I'm on OSX btw).

@saghul
Copy link
Member

saghul commented Jul 22, 2015

So, you tested the test without the rest of the patch? No idea, cannot try it right now, sorry.

@ronkorving
Copy link
Contributor Author

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.

@saghul
Copy link
Member

saghul commented Jul 22, 2015

I think you have mixed up your branches. There is no such a test in the v1.x branch. This very PR adds it.

@ronkorving
Copy link
Contributor Author

Sigh. It must be Wednesday. I never could get the hang of Wednesdays.

@saghul
Copy link
Member

saghul commented Jul 23, 2015

Looking good! Can you please add another test which uses some initial offset please? Thanks!

@saghul
Copy link
Member

saghul commented Aug 3, 2015

@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?

@ronkorving
Copy link
Contributor Author

Oh really? Hurray! About to catch to some sleep, but will do that in about 11 hours. Thanks for everything @saghul!

@ronkorving ronkorving force-pushed the iovmax branch 3 times, most recently from 00b3bd1 to cf03f1b Compare August 4, 2015 02:28
@ronkorving
Copy link
Contributor Author

@saghul squashed the lot.

@saghul
Copy link
Member

saghul commented Aug 4, 2015

@ronkorving I still see 5 commits. Please make it one and adjust the commit log to our guidelines in CONTRIBUTING.md. Thanks!

@ronkorving
Copy link
Contributor Author

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.

@ronkorving
Copy link
Contributor Author

That's it, everything in a single commit.

test/test-fs.c Outdated
Copy link
Member

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.

@ronkorving
Copy link
Contributor Author

I messed up, give me a moment.

@ronkorving ronkorving force-pushed the iovmax branch 3 times, most recently from 7c8f5e4 to 9bd3975 Compare August 4, 2015 08:40
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.
@ronkorving
Copy link
Contributor Author

All done.

@saghul
Copy link
Member

saghul commented Aug 4, 2015

@ronkorving
Copy link
Contributor Author

Are we there yet? :) CI looks promising.

@saghul
Copy link
Member

saghul commented Aug 4, 2015

We are. LGTM, landing now.

saghul pushed a commit that referenced this pull request Aug 4, 2015
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]>
@saghul
Copy link
Member

saghul commented Aug 4, 2015

Landed in ✨ 2bf7827 ✨ Thanks a lot @ronkorving and @bwijen!

@saghul saghul closed this Aug 4, 2015
@ronkorving
Copy link
Contributor Author

\o/ Thanks guys!
Looking forward to the next release :)

skomski pushed a commit to skomski/libuv that referenced this pull request Aug 5, 2015
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]>
skomski pushed a commit to skomski/libuv that referenced this pull request Aug 5, 2015
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]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants