Skip to content
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

multi_wait improvements #13150

Closed
wants to merge 3 commits into from
Closed

multi_wait improvements #13150

wants to merge 3 commits into from

Conversation

icing
Copy link
Contributor

@icing icing commented Mar 19, 2024

In multi_wait(), which is called all the time, multi_getsock() was called twice.

The first invocation was used to count the number of sockets, a sufficiently large struct pollfd array was allocated, and then the second invocation was used to populate that array. This is not only an overhead, but it assumes that the second call will deliver the same number of sockets as the first. Which is not really guaranteed. Also, when parallel transfers on the same connection are active, the same socket is collected for every transfer.

  • only call multi_getsock() once for all transfers. Grow the struct pollfd array when needed.
  • fold repeated sockets. This is done cheaply by just comparing the current socket to the last one. It will not catch all duplicates on multiple connections.

@icing
Copy link
Contributor Author

icing commented Mar 20, 2024

@jay: I'd like your review on this because it touches on the Window parts which I tried to preserve to the best of my understanding.

lib/multi.c Outdated Show resolved Hide resolved
#ifdef USE_WINSOCK
if(mask) {
if(WSAEventSelect(ps.sockets[i], multi->wsa_event, mask) != 0) {
Copy link
Member

Choose a reason for hiding this comment

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

@mback2k is WSAEventSelect supposed to be called even when mask is 0?

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 think mask == 0 is used to remove a socket again. See line 1475 after the poll.

The check I added is for the case where a socket is returned, but neither POLLIN, nor POLLOUT is set. Should not happen, but needs to be handled. Calling WSAEventSelect() with 0 would then remove a socket and that is not correct in that situation. The socket might have been added before by another transfer.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thinking about this, I am not even sure this works correctly as is for parallel transfers. Think about this case: transfers A and B on the same connection socket X.

  • A wants to POLLIN on X
  • B wants to POLLOUT on X

Does calling WSAEventSelect() on X not override the previous mask?

Copy link
Member

Choose a reason for hiding this comment

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

i recall there was some issue with wsa events, where marc had added it to purposely reset the flags.

Does calling WSAEventSelect() on X not override the previous mask?

it does override so if you want to wait on read and write you have to do it together. i hope marc will have time to review this pr, he has been busy so give it a few days

fwiw i used a build of this pr yesterday with many parallel http/2 httpbin drip replies and didn't see any difference in behavior

Copy link
Member

Choose a reason for hiding this comment

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

@jay you are right regarding the resetting of flags needing to happen, but this comes later in the existing code:

curl/lib/multi.c

Line 1428 in a41cd15

WSAEventSelect(s, multi->wsa_event, 0);

That is why a little bit above that line I left this comment:

curl/lib/multi.c

Lines 1405 to 1407 in a41cd15

/* With WinSock, we have to run the following section unconditionally
to call WSAEventSelect(fd, event, 0) on all the sockets */
{

Does calling WSAEventSelect() on X not override the previous mask?

Yes, it will overwrite the previous mask for the same multi->wsa_event. Anyway, I think your new check referenced by this review thread is fine, as mask == 0 should not happen at this point.

Copy link
Member

@mback2k mback2k Mar 30, 2024

Choose a reason for hiding this comment

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

Thinking about this, I am not even sure this works correctly as is for parallel transfers.

I am not into the multi-handle details here, would each transfer have its own multi-handle and therefore own multi->wsa_event? If not, you are probably right and the WSA event will need to be changed to be per-transfer if multi_wait is called individually per-transfer.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There can be many transfers on the same multihandle and many transfers on the same connection (HTTP/2, HTTP/3). These report the connection socket with their individual needs.

When transfer A and B are on the same connection, A might request POLLIN and B POLLOUT. Without coalescing the two, the masks will overwrite each other.

Copy link
Member

Choose a reason for hiding this comment

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

The main question is if multi_wait will be executed in parallel for these transfers on one multi handle. Also, I am no sure what will happen if the WSA event is changed while already being waited on in another thread for example. Maybe that is undefined behavior or not supported after all?

Copy link
Member

Choose a reason for hiding this comment

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

if multi_wait will be executed in parallel for these transfers on one multi handle

It cannot be executed in parallel for the same handle, no.

@mback2k
Copy link
Member

mback2k commented Mar 26, 2024

Sorry, I won't be able to look at this before the weekend.

lib/multi.c Show resolved Hide resolved
lib/multi.c Show resolved Hide resolved
@bagder
Copy link
Member

bagder commented Apr 11, 2024

@mback2k @jay what's your take on this now? It would be neat to merge, but I don't sense that we have your 👍 on it just yet?

@calvin2021y
Copy link

this weekend I try test this patch, find it cannot merge into master.

    - only call `multi_getsock()` once for all transfers
    - realloc pollset array on demand
    - fold repeated sockets
- add args to scorecard.py to control max parallel downloads
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging this pull request may close these issues.

None yet

5 participants