Skip to content

Ensure LocalEndPoint being updated in SendToAsync(SocketAsyncEventArgs)#808

Merged
antonfirsov merged 11 commits intomasterfrom
fix-26882
Jan 8, 2020
Merged

Ensure LocalEndPoint being updated in SendToAsync(SocketAsyncEventArgs)#808
antonfirsov merged 11 commits intomasterfrom
fix-26882

Conversation

@antonfirsov
Copy link
Contributor

@antonfirsov antonfirsov commented Dec 12, 2019

Fixes #915 by assigning _rightEndPoint like in other overloads, introducing a breaking change in the behavior of SendToAsync(SocketAsyncEventArgs).

Fixes #915

@dnfclas
Copy link

dnfclas commented Dec 12, 2019

CLA assistant check
All CLA requirements met.

Comment on lines +4125 to +4128
if (_rightEndPoint == null)
{
_rightEndPoint = endPointSnapshot;
}
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 was not sure at which point should this happen. BeginSendTo assigns it before doing the actual WSA P/Invoke, I tried to follow that approach.

Copy link
Member

Choose a reason for hiding this comment

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

Nit: for future reference, this can now be written as:

_rightEndPoint ??= endPointSnapshot;

@davidsh davidsh added the breaking-change Issue or PR that represents a breaking API or functional change over a previous release. label Dec 12, 2019
@davidsh davidsh added this to the 5.0 milestone Dec 12, 2019
Task.Run(() => s.Receive((Span<byte>)buffer, SocketFlags.None));
public override Task<int> SendAsync(Socket s, ArraySegment<byte> buffer) =>
Task.Run(() => s.Send((ReadOnlySpan<byte>)buffer, SocketFlags.None));
public override bool UsesSync => true;
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 guess this was missing by mistake. The property seemed useful for my tests.

@antonfirsov antonfirsov requested a review from a team December 12, 2019 18:39
@antonfirsov
Copy link
Contributor Author

antonfirsov commented Dec 12, 2019

@scalablecory @davidsh I just realized that my current changes are repeating the mistake pointed out in dotnet/corefx#40952. My understanding is that simply replacing the conditional block with Interlocked.CompareExchange would fix the race. Is this correct?

Can I address both issues in this PR, or shall I open a separate one?

@scalablecory
Copy link
Contributor

@scalablecory @davidsh I just realized that my current changes are repeating the mistake pointed out in dotnet/corefx#40952. My understanding is that simply replacing the conditional block with Interlocked.CompareExchange would fix the race. Is this correct?

It's going to need more than a CAS. Here's what I'm seeing (each snip being a context switch)

  1. user calls BeginRecieveFrom, and _rightEndPoint gets set.

if (_rightEndPoint == null)
{
_rightEndPoint = endPointSnapshot;
}

  1. user calls BeginSendTo and _rightEndPoint is already set, so it is left alone.

if (_rightEndPoint == null)
{
_rightEndPoint = endPointSnapshot;
}

  1. BeginReceiveFrom now fails and unsets _rightEndPoint

catch (ObjectDisposedException)
{
_rightEndPoint = oldEndPoint;
throw;
}

  1. BeginSendTo succeeds, and _rightEndPoint is null, so LocalEndPoint will return null despite the socket now being implicitly bound.

Can I address both issues in this PR, or shall I open a separate one?

I think that's fine.

@antonfirsov antonfirsov added the NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) label Dec 12, 2019
@antonfirsov
Copy link
Contributor Author

/azp run runtime-libraries outerloop

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@antonfirsov antonfirsov removed the NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) label Dec 17, 2019
@antonfirsov
Copy link
Contributor Author

antonfirsov commented Dec 17, 2019

@dotnet/ncl after a further analysis testing of the code, my opinion got even stronger that we should handle the two topics separately. After we decide exactly how we change the way _rightEndpoint is being handled, we can change all it's usages in a separate PR.

I removed the no merge label, and marking this PR ready to review as is.

@antonfirsov
Copy link
Contributor Author

A WebSocket and an Http outerloop test failed on linux.
I'm not familiar with those codebases yet, but I think it's very unlikely that they utilize SendToAsync(SocketAsyncEventArgs).

Copy link
Contributor

@scalablecory scalablecory left a comment

Choose a reason for hiding this comment

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

The other methods null out _rightEndPoint if the API call fails. We should be doing this here too, in the catch block below your change.

@wfurt
Copy link
Member

wfurt commented Dec 18, 2019

fixes #915

Comment on lines 4136 to +4145
catch
{
_rightEndPoint = null;
// Clear in-use flag on event args object.
e.Complete();
throw;
}

if (!CheckErrorAndUpdateStatus(socketError))
{
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 have no idea what black magic could trigger the catch block here, on the other hand socketError == AccessDenied is something we can actually hit, see my test cases. Like in DoBeginSendTo I'm resetting _rightEndPoint when hitting a non-success & non-pending case.

@antonfirsov
Copy link
Contributor Author

@scalablecory just addressed your findings + did some test improvements:

  • Moved the new SendTo tests to it's own file & test classes
  • Added missing argument validation tests for all SendTo variants

Copy link
Contributor

@scalablecory scalablecory left a comment

Choose a reason for hiding this comment

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

lgtm

}
}


Copy link
Contributor

Choose a reason for hiding this comment

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

nit: extra line.

@antonfirsov
Copy link
Contributor Author

Test failures are tracked under #1332 and unrelated to Sockets.

@antonfirsov antonfirsov merged commit a42d3a3 into master Jan 8, 2020
@antonfirsov antonfirsov deleted the fix-26882 branch January 8, 2020 13:47
@danmoseley
Copy link
Member

@antonfirsov is this missing a breaking change doc issue? If so can you please open one with https://github.com/dotnet/docs/issues/new?template=dotnet-breaking-change.md

@danmoseley danmoseley added the needs-breaking-change-doc-created Breaking changes need an issue opened with https://github.com/dotnet/docs/issues/new?template=dotnet label Jul 30, 2020
@antonfirsov antonfirsov removed the needs-breaking-change-doc-created Breaking changes need an issue opened with https://github.com/dotnet/docs/issues/new?template=dotnet label Oct 6, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 11, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Net.Sockets breaking-change Issue or PR that represents a breaking API or functional change over a previous release.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Socket LocalEndPoint not updated in SendToAsync(SocketAsyncEventArgs e)

8 participants