-
Notifications
You must be signed in to change notification settings - Fork 162
Open
Description
Repro
[Fact]
public async Task ConnectionLost_MoveNext()
{
await foreach (int number in this.clientProxy.Value.GetNumbersAsync(this.TimeoutToken))
{
}
// Simulate a connection loss by disposing the server.
this.serverRpc.Dispose();
await using var enumerator = this.clientProxy.Value.GetNumbersAsync(this.TimeoutToken)
.GetAsyncEnumerator(this.TimeoutToken);
try
{
await enumerator.MoveNextAsync();
} catch (ConnectionLostException e)
{
return;
}
}
Expected
Test passes
Actual
Test fails with
StreamJsonRpc.ConnectionLostException : The JSON-RPC connection with the remote party was lost before the request could complete.
---- System.OperationCanceledException : The operation was canceled.
Stack Trace:
JsonRpc.InvokeCoreAsync(JsonRpcRequest request, Type expectedResultType, CancellationToken cancellationToken) line 1982
JsonRpc.InvokeCoreAsync[TResult](RequestId id, String targetName, IReadOnlyList`1 arguments, IReadOnlyList`1 positionalArgumentDeclaredTypes, IReadOnlyDictionary`2 namedArgumentDeclaredTypes, CancellationToken cancellationToken, Boolean isParameterObject) line 1564
<<-ctor>b__0>d.MoveNext() line 1076
--- End of stack trace from previous location ---
ExecuteContinuationSynchronouslyAwaiter`1.GetResult()
<<GetValueAsync>b__0>d.MoveNext()
--- End of stack trace from previous location ---
AsyncEnumeratorProxy.DisposeAsync() line 1102
AsyncEnumerableTests.ConnectionLost_MoveNext() line 469
--- End of stack trace from previous location ---
----- Inner Stack Trace -----
CancellationToken.ThrowOperationCanceledException()
CancellationToken.ThrowIfCancellationRequested()
MessageHandlerBase.WriteAsync(JsonRpcMessage content, CancellationToken cancellationToken) line 218
JsonRpc.SendAsync(JsonRpcMessage message, CancellationToken cancellationToken) line 1720
JsonRpc.InvokeCoreAsync(JsonRpcRequest request, Type expectedResultType, CancellationToken cancellationToken) line 1949
Remarks
This happens because DisposeAsync
throws when connection is already gone.
It breaks ergonomics on using async iterators as you can no longer rely on await using var iterator = ...GetAsyncEnumerator
to correctly dispose the iterator on failure - you have to wrap the whole thing in try ... catch
. Normally you only have to do it for MoveNextAsync
.
It also violates CA1065
Metadata
Metadata
Assignees
Labels
No labels