Skip to content

Commit 5586d18

Browse files
committed
Code review feedback
1 parent 2ea2729 commit 5586d18

File tree

2 files changed

+25
-32
lines changed

2 files changed

+25
-32
lines changed

src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/ThrowHelper.cs

+3
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ internal static bool TryGetStreamExceptionForMsQuicStatus(int status, [NotNullWh
6464
return false;
6565
}
6666

67+
// see TryGetStreamExceptionForMsQuicStatus for explanation
68+
internal static bool IsConnectionAbortedWhenStartingStreamException(Exception ex) => ex is QuicException qe && qe.QuicError == QuicError.ConnectionAborted && qe.ApplicationErrorCode is null;
69+
6770
internal static Exception GetExceptionForMsQuicStatus(int status, long? errorCode = default, string? message = null)
6871
{
6972
Exception ex = GetExceptionInternal(status, errorCode, message);

src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs

+22-32
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ static async ValueTask<QuicConnection> StartConnectAsync(QuicClientConnectionOpt
108108
/// </summary>
109109
private int _disposed;
110110

111+
/// <summary>
112+
/// Completed when connection shutdown is initiated.
113+
/// </summary>
114+
private TaskCompletionSource _connectionCloseTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
115+
111116
private readonly ValueTaskSource _connectedTcs = new ValueTaskSource();
112117
private readonly ResettableValueTaskSource _shutdownTcs = new ResettableValueTaskSource()
113118
{
@@ -435,15 +440,14 @@ public async ValueTask<QuicStream> OpenOutboundStreamAsync(QuicStreamType type,
435440
ObjectDisposedException.ThrowIf(_disposed == 1, this);
436441

437442
// In case of an incoming race when the connection is closed by the peer just before we open the stream,
438-
// we receive QUIC_STATUS_ABORTED from MsQuic, but we don't know how the connection was closed. To
439-
// distinguish this case, we throw ConnectionAborted without ApplicationErrorCode. In such a case, we
440-
// can expect the connection close exception to be already reported on the connection level (or very soon).
441-
bool connectionAbortedByPeer = ex is QuicException qe && qe.QuicError == QuicError.ConnectionAborted && qe.ApplicationErrorCode is null;
443+
// we receive QUIC_STATUS_ABORTED from MsQuic, but we don't know how the connection was closed. We throw
444+
// special exception and handle it here where we can determine the shutdown reason.
445+
bool connectionAbortedByPeer = ThrowHelper.IsConnectionAbortedWhenStartingStreamException(ex);
442446

443447
// Propagate connection error if present.
444-
if (_acceptQueue.Reader.Completion.IsFaulted || connectionAbortedByPeer)
448+
if (_connectionCloseTcs.Task.IsFaulted || connectionAbortedByPeer)
445449
{
446-
await _acceptQueue.Reader.Completion.ConfigureAwait(false);
450+
await _connectionCloseTcs.Task.ConfigureAwait(false);
447451
}
448452
throw;
449453
}
@@ -541,12 +545,15 @@ private unsafe int HandleEventShutdownInitiatedByTransport(ref SHUTDOWN_INITIATE
541545
{
542546
Exception exception = ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetExceptionForMsQuicStatus(data.Status, (long)data.ErrorCode));
543547
_connectedTcs.TrySetException(exception);
544-
CompleteAcceptQueue(exception, false);
548+
_connectionCloseTcs.TrySetException(exception);
549+
_acceptQueue.Writer.TryComplete(exception);
545550
return QUIC_STATUS_SUCCESS;
546551
}
547552
private unsafe int HandleEventShutdownInitiatedByPeer(ref SHUTDOWN_INITIATED_BY_PEER_DATA data)
548553
{
549-
CompleteAcceptQueue(ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetConnectionAbortedException((long)data.ErrorCode)), false);
554+
Exception exception = ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetConnectionAbortedException((long)data.ErrorCode));
555+
_connectionCloseTcs.TrySetException(exception);
556+
_acceptQueue.Writer.TryComplete(exception);
550557
return QUIC_STATUS_SUCCESS;
551558
}
552559
private unsafe int HandleEventShutdownComplete()
@@ -555,7 +562,8 @@ private unsafe int HandleEventShutdownComplete()
555562
_tlsSecret?.WriteSecret();
556563

557564
Exception exception = ExceptionDispatchInfo.SetCurrentStackTrace(_disposed == 1 ? new ObjectDisposedException(GetType().FullName) : ThrowHelper.GetOperationAbortedException());
558-
CompleteAcceptQueue(exception, true);
565+
_connectionCloseTcs.TrySetException(exception);
566+
_acceptQueue.Writer.TryComplete(exception);
559567
_connectedTcs.TrySetException(exception);
560568
_shutdownTokenSource.Cancel();
561569
_shutdownTcs.TrySetResult(final: true);
@@ -666,28 +674,6 @@ private static unsafe int NativeCallback(QUIC_HANDLE* connection, void* context,
666674
}
667675
}
668676

669-
private void CompleteAcceptQueue(Exception? ex, bool drain)
670-
{
671-
_acceptQueue.Writer.TryComplete(ex);
672-
673-
if (drain)
674-
{
675-
// This should be only called after connection SHUTDOWN_COMPLETE has been indicated.
676-
// At that point, all streams have been already shut down internally and we need
677-
// only to close the handle via dispose, so DisposeAsync below should complete
678-
// synchronously (which is necessary for this method to be callable from MsQuic
679-
// event callback).
680-
while (_acceptQueue.Reader.TryRead(out QuicStream? stream))
681-
{
682-
ValueTask task = stream.DisposeAsync();
683-
Debug.Assert(task.IsCompletedSuccessfully);
684-
task.GetAwaiter().GetResult();
685-
}
686-
687-
Debug.Assert(_acceptQueue.Reader.Completion.IsCompleted);
688-
}
689-
}
690-
691677
/// <summary>
692678
/// If not closed explicitly by <see cref="CloseAsync(long, CancellationToken)" />, closes the connection with the <see cref="QuicConnectionOptions.DefaultCloseErrorCode"/>.
693679
/// And releases all resources associated with the connection.
@@ -741,6 +727,10 @@ public async ValueTask DisposeAsync()
741727
}
742728

743729
// Flush the queue and dispose all remaining streams.
744-
CompleteAcceptQueue(ExceptionDispatchInfo.SetCurrentStackTrace(new ObjectDisposedException(GetType().FullName)), true);
730+
_acceptQueue.Writer.TryComplete(ExceptionDispatchInfo.SetCurrentStackTrace(new ObjectDisposedException(GetType().FullName)));
731+
while (_acceptQueue.Reader.TryRead(out QuicStream? stream))
732+
{
733+
await stream.DisposeAsync().ConfigureAwait(false);
734+
}
745735
}
746736
}

0 commit comments

Comments
 (0)