Skip to content

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

@antonfirsov

Description

@antonfirsov

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions