@@ -98,6 +98,11 @@ static async ValueTask<QuicConnection> StartConnectAsync(QuicClientConnectionOpt
9898 /// </summary>
9999 private int _disposed ;
100100
101+ /// <summary>
102+ /// Completed when connection shutdown is initiated.
103+ /// </summary>
104+ private TaskCompletionSource _connectionCloseTcs = new TaskCompletionSource ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
105+
101106 private readonly ValueTaskSource _connectedTcs = new ValueTaskSource ( ) ;
102107 private readonly ValueTaskSource _shutdownTcs = new ValueTaskSource ( ) ;
103108
@@ -376,16 +381,25 @@ public async ValueTask<QuicStream> OpenOutboundStreamAsync(QuicStreamType type,
376381 stream = new QuicStream ( _handle , type , _defaultStreamErrorCode ) ;
377382 await stream . StartAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
378383 }
379- catch
384+ catch ( Exception ex )
380385 {
381386 if ( stream is not null )
382387 {
383388 await stream . DisposeAsync ( ) . ConfigureAwait ( false ) ;
384389 }
390+
391+ // Propagate ODE if disposed in the meantime.
392+ ObjectDisposedException . ThrowIf ( _disposed == 1 , this ) ;
393+
394+ // In case of an incoming race when the connection is closed by the peer just before we open the stream,
395+ // we receive QUIC_STATUS_ABORTED from MsQuic, but we don't know how the connection was closed. We throw
396+ // special exception and handle it here where we can determine the shutdown reason.
397+ bool connectionAbortedByPeer = ThrowHelper . IsConnectionAbortedWhenStartingStreamException ( ex ) ;
398+
385399 // Propagate connection error if present.
386- if ( _acceptQueue . Reader . Completion . IsFaulted )
400+ if ( _connectionCloseTcs . Task . IsFaulted || connectionAbortedByPeer )
387401 {
388- await _acceptQueue . Reader . Completion . ConfigureAwait ( false ) ;
402+ await _connectionCloseTcs . Task . ConfigureAwait ( false ) ;
389403 }
390404 throw ;
391405 }
@@ -475,17 +489,21 @@ private unsafe int HandleEventShutdownInitiatedByTransport(ref SHUTDOWN_INITIATE
475489 {
476490 Exception exception = ExceptionDispatchInfo . SetCurrentStackTrace ( ThrowHelper . GetExceptionForMsQuicStatus ( data . Status , ( long ) data . ErrorCode ) ) ;
477491 _connectedTcs . TrySetException ( exception ) ;
492+ _connectionCloseTcs . TrySetException ( exception ) ;
478493 _acceptQueue . Writer . TryComplete ( exception ) ;
479494 return QUIC_STATUS_SUCCESS ;
480495 }
481496 private unsafe int HandleEventShutdownInitiatedByPeer ( ref SHUTDOWN_INITIATED_BY_PEER_DATA data )
482497 {
483- _acceptQueue . Writer . TryComplete ( ExceptionDispatchInfo . SetCurrentStackTrace ( ThrowHelper . GetConnectionAbortedException ( ( long ) data . ErrorCode ) ) ) ;
498+ Exception exception = ExceptionDispatchInfo . SetCurrentStackTrace ( ThrowHelper . GetConnectionAbortedException ( ( long ) data . ErrorCode ) ) ;
499+ _connectionCloseTcs . TrySetException ( exception ) ;
500+ _acceptQueue . Writer . TryComplete ( exception ) ;
484501 return QUIC_STATUS_SUCCESS ;
485502 }
486503 private unsafe int HandleEventShutdownComplete ( )
487504 {
488- Exception exception = ExceptionDispatchInfo . SetCurrentStackTrace ( ThrowHelper . GetOperationAbortedException ( ) ) ;
505+ Exception exception = ExceptionDispatchInfo . SetCurrentStackTrace ( _disposed == 1 ? new ObjectDisposedException ( GetType ( ) . FullName ) : ThrowHelper . GetOperationAbortedException ( ) ) ;
506+ _connectionCloseTcs . TrySetException ( exception ) ;
489507 _acceptQueue . Writer . TryComplete ( exception ) ;
490508 _connectedTcs . TrySetException ( exception ) ;
491509 _shutdownTcs . TrySetResult ( ) ;
0 commit comments