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

Socket.Begin*** methods wrapping Task variants do not throw SocketExceptions synchronously #47905

Closed
antonfirsov opened this issue Feb 5, 2021 · 7 comments
Labels
area-System.Net.Sockets bug tenet-compatibility Incompatibility with previous versions or .NET Framework
Milestone

Comments

@antonfirsov
Copy link
Member

antonfirsov commented Feb 5, 2021

The patterns we started to use while implementing #43845 are silently introducing a breaking change.

Previously, when a native socket method (eg. WSASend) returned an error within a Begin*** call, we were throwing SocketException synchronously:

IAsyncResult? result = BeginSend(buffer, offset, size, socketFlags, out errorCode, callback, state);
if (errorCode != SocketError.Success && errorCode != SocketError.IOPending)
{
throw new SocketException((int)errorCode);
}

We are no longer doing this in the methods we refactored to wrap Tasks. We are just forwarding the failed task to an IAsyncResult:

return TaskToApm.Begin(SendAsync(new ReadOnlyMemory<byte>(buffer, offset, size), socketFlags, default).AsTask(), callback, state);

The Task/ValueTask variants are (by design?) not throwing on sync path. This means that the APM exceptions will be only observed when invoking the corresponding End*** calls, even if the error has been reported synchronously.

We should either switch back to old exception behavior in the APM methods, or announce this as a breaking change.

Note that this is a poorly covered functionality. The refactor work did not lead to any test failure until #47781, and even there, we only encountered it one outerloop test.

/cc @geoffkizer

@dotnet-issue-labeler dotnet-issue-labeler bot added area-System.Net.Sockets untriaged New issue has not been triaged by the area owner labels Feb 5, 2021
@ghost
Copy link

ghost commented Feb 5, 2021

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

Issue Details

The patterns we started to use while implementing #43845 are silently introducing a breaking change.

Previously, when a native socket method (eg. WSASend) returned an error within a Begin*** call, we were throwing SocketException synchronously:

IAsyncResult? result = BeginSend(buffer, offset, size, socketFlags, out errorCode, callback, state);
if (errorCode != SocketError.Success && errorCode != SocketError.IOPending)
{
throw new SocketException((int)errorCode);
}

We are no longer doing this in the methods we refactored to wrap Task. We are just wrapping the failed task into an IAsyncResult:

return TaskToApm.Begin(SendAsync(new ReadOnlyMemory<byte>(buffer, offset, size), socketFlags, default).AsTask(), callback, state);

The Task/ValueTask variants are (by design?) not throwing on sync path. This means that the APM exceptions will be only observed when invoking the corresponding End*** calls, even if the error has been reported synchronously.

We should either switch back to old exception behavior in the APM methods, or announce this as a breaking change.

Note that this is a poorly covered functionality. The refactor work did not lead to any test failure until #47781, and even there, we only encountered it one outerloop test.

/cc @geoffkizer

Author: antonfirsov
Assignees: -
Labels:

area-System.Net.Sockets, untriaged

Milestone: -

@geoffkizer
Copy link
Contributor

We should maintain the existing behavior here if it's not too much trouble, i.e. throw from Begin* for sync failure. Again, I think this is counter to the usual IAsyncResult pattern, which is why the helpers don't do this. But it's better to maintain existing behavior.

FWIW, it would also be interesting to look at .NET Framework behavior here. Early in .NET Core (2.0?) we made a change so that operations that complete synchronously at the OS level would actually complete synchronously at the .NET level, i.e. IAsyncResult/Task. I can't recall how exception behavior was handled before this change, though -- it may be that exceptions were still synchronous and were thrown from Begin even before this change. Either way, it would be good to know for sure. If .NET Framework doesn't throw from Begin in these cases, then that's a good reason to do the same for .NET Core, even if that's different than past behavior of .NET Core.

@antonfirsov
Copy link
Member Author

antonfirsov commented Feb 8, 2021

Looks like we are throwing synchronously in .NET Framework:
https://referencesource.microsoft.com/#System/net/System/Net/Sockets/Socket.cs,3430
https://referencesource.microsoft.com/#System/net/System/Net/Sockets/Socket.cs,4066

I also think, we should revert to the old behavior, so triaging this issue to 6.0. I believe the best is if we fix this before pushing in any further overloads.

@antonfirsov antonfirsov added this to the 6.0.0 milestone Feb 8, 2021
@antonfirsov antonfirsov removed the untriaged New issue has not been triaged by the area owner label Feb 8, 2021
antonfirsov added a commit to antonfirsov/runtime that referenced this issue Feb 8, 2021
@stephentoub
Copy link
Member

we should revert to the old behavior

FWIW, I wouldn't invest a ton of time and energy into doing so, nor would I jeopardize the behavior the Task-based methods to do so.

@antonfirsov
Copy link
Member Author

I don't think it's a ton of time and energy to reintroduce this for the APM methods. I didn't mean to change the Task-based overloads. Putting back the untriaged label since there is no consensus.

@antonfirsov antonfirsov added the untriaged New issue has not been triaged by the area owner label Feb 8, 2021
@karelz karelz added bug tenet-compatibility Incompatibility with previous versions or .NET Framework labels Feb 17, 2021
@karelz
Copy link
Member

karelz commented Feb 17, 2021

Triage: Looks like regression we introduced during 6.0. Low cost, we should do it and prevent 6.0 from shipping it.
If cost goes higher, we can revisit.

@karelz karelz removed the untriaged New issue has not been triaged by the area owner label Feb 17, 2021
@ghost ghost added in-pr There is an active PR which will close this issue when it is merged and removed in-pr There is an active PR which will close this issue when it is merged labels Apr 22, 2021
@antonfirsov
Copy link
Member Author

As per discussion under #51212 (comment), I'm closing this in favor of dotnet/dotnet-api-docs#6658.

@ghost ghost locked as resolved and limited conversation to collaborators May 22, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Net.Sockets bug tenet-compatibility Incompatibility with previous versions or .NET Framework
Projects
None yet
4 participants