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

add support for parallel Connect #106374

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft

add support for parallel Connect #106374

wants to merge 4 commits into from

Conversation

wfurt
Copy link
Member

@wfurt wfurt commented Aug 14, 2024

this adds API approved in #87932. It seems like this only supports SAEA. We may consider extending it to be also supported with Task.

To limit possible risk, I added new member to existing and transient MultiConnectSocketAsyncEventArgs e.g. there should not be any penalty for Socket. We also originally discussed some split DNS query with @liveans and that can be possibly done separately. This is basic snapshot I crafted when hoping to squeeze this to 9.0.

Testing is tricky as it depends on DNS entries and particular behavior and speed.

@wfurt wfurt added NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) area-System.Net.Sockets labels Aug 14, 2024
@wfurt wfurt added this to the 10.0.0 milestone Aug 14, 2024
@wfurt wfurt requested a review from a team August 14, 2024 00:15
Copy link

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

Copy link

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

Copy link
Contributor

Tagging subscribers to this area: @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

@liveans
Copy link
Member

liveans commented Aug 14, 2024

I'm not sure about the behavior of Dns.Get* APIs with AddressFamily.Unspecified.
If it blocks until we get a response from both AddressFamily, then this might be problem here.

Because if either IPv6/IPv4 response is late or timeouts somehow, then it means we're delaying connection until we got both of the responses, this can be problematic for some specific cases.

But on the other hand with this approach, OS is implementing Destination Address Selection Algorithm for us, and we don't have code complexity here, if we decide to go to splitting DNS queries we might need to consider implementing Destination Address Selection Algorithm.

}
else
{
if (nextAddressIndex == -1 && nextIPv6Addressindex == -1)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (nextAddressIndex == -1 && nextIPv6Addressindex == -1)
if (nextAddressIndex < 0 && nextIPv6Addressindex < 0)

The JIT can produce less and more optimized machine-code (if it matters here too, but the change is cheap...)

@@ -833,13 +1001,13 @@ async Task Core(MultiConnectSocketAsyncEventArgs internalArgs, Task<IPAddress[]>
// Store the results.
if (caughtException != null)
{
SetResults(caughtException, 0, SocketFlags.None);
SetResults(caughtException, 0, SocketFlags.None);
Copy link
Member

Choose a reason for hiding this comment

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

Nit: there's a space too much

Suggested change
SetResults(caughtException, 0, SocketFlags.None);
SetResults(caughtException, 0, SocketFlags.None);

@@ -1087,6 +1087,84 @@ public async Task SendTo_DifferentEP_Success(bool ipv4)
result = await receiver2.ReceiveFromAsync(receiveBuffer, remoteEp).WaitAsync(TestSettings.PassingTestTimeout);
Assert.Equal(sendBuffer.Length, result.ReceivedBytes);
}

[ConditionalFact(typeof(DualModeBase), nameof(DualModeBase.LocalhostIsBothIPv4AndIPv6))]
public void Connect_Parallel_Succss()
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
public void Connect_Parallel_Succss()
public void Connect_Parallel_Success()


int nextAddressIndex = 0;
int nextIPv6AddressIndex = GetNextAddress(addresses, AddressFamily.InterNetworkV6, -1);
int nextUPv4AddressIndex = GetNextAddress(addresses, AddressFamily.InterNetwork, -1);
Copy link
Member

Choose a reason for hiding this comment

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

Looks like this variable only used within parallelConnect variable and that's it, perhaps delete this and call function directly in parallelConnect.

{
address = addresses[nextAddressIndex];
nextAddressIndex = GetNextAddress(addresses, currentAddressFamily, nextAddressIndex);

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change

Comment on lines +786 to +787
}
internalArgs.SocketError = SocketError.IOPending;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
}
internalArgs.SocketError = SocketError.IOPending;
}
internalArgs.SocketError = SocketError.IOPending;

address = addresses[nextIPv6AddressIndex];
internalArgs.SocketError = SocketError.IOPending;
address2 = null;
internalArgs.SecondarySaea = null;
Copy link
Member

Choose a reason for hiding this comment

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

Should we dispose this before null out?

{
if (nextIPv6AddressIndex >= 0)
{
address2 = addresses[nextIPv6AddressIndex];
Copy link
Member

Choose a reason for hiding this comment

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

There is duplicate assign on line 806

break;
}

if (internalArgs.SocketError != SocketError.IOPending)
Copy link
Member

Choose a reason for hiding this comment

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

Can we be more specific with this check, and propagate the error if the error is something else other than expected (e.g. SocketError.SystemNotReady) ?

if (nextIPv6AddressIndex >= 0)
{
address2 = addresses[nextIPv6AddressIndex];
if (address == null)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (address == null)
if (address is not null)

// defaul mechanism e.g. sequential processing
Default = 0,

// use a Happy Eyeballs-like algorithm to connect.
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Please use XML comments

public static bool ConnectAsync(SocketType socketType, ProtocolType protocolType, SocketAsyncEventArgs e)
public static bool ConnectAsync(SocketType socketType, ProtocolType protocolType, SocketAsyncEventArgs e) =>
ConnectAsync(socketType, protocolType, e, ConnectAlgorithm.Default);
public static bool ConnectAsync(SocketType socketType, ProtocolType protocolType, SocketAsyncEventArgs e, ConnectAlgorithm connectAlgorithm)
Copy link
Member

Choose a reason for hiding this comment

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

Validate that connectAlgorithm is one of the two known values?

int nextUPv4AddressIndex = GetNextAddress(addresses, AddressFamily.InterNetwork, -1);


// We can do parallel connect only if siocket was not specified and when there is at least one address of each AF.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// We can do parallel connect only if siocket was not specified and when there is at least one address of each AF.
// We can do parallel connect only if socket was not specified and when there is at least one address of each AF.

{
if (!parallelConnect)
{
// We simply try addresses in sequence until we either try them all or operation is cancelled.
Copy link
Member

Choose a reason for hiding this comment

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

Nit:

Suggested change
// We simply try addresses in sequence until we either try them all or operation is cancelled.
// We simply try addresses in sequence until we either try them all or operation is canceled.

@@ -672,12 +672,27 @@ internal void FinishOperationAsyncFailure(SocketError socketError, int bytesTran
}
}

private static int GetNextAddress(IPAddress[] addresses, AddressFamily addressFamily, int index)
{
for (int i = index + 1; i < addresses.Length; i++)
Copy link
Member

Choose a reason for hiding this comment

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

Why the +1 and then using -1 later?

@wfurt wfurt removed the NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) label Sep 9, 2024
@wfurt wfurt marked this pull request as draft September 10, 2024 01:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants