@@ -525,12 +525,12 @@ private unsafe int HandleEventShutdownInitiatedByTransport(ref SHUTDOWN_INITIATE
525525 {
526526 Exception exception = ExceptionDispatchInfo . SetCurrentStackTrace ( ThrowHelper . GetExceptionForMsQuicStatus ( data . Status , ( long ) data . ErrorCode ) ) ;
527527 _connectedTcs . TrySetException ( exception ) ;
528- CompleteAndDrainAcceptQueue ( exception ) ;
528+ CompleteAcceptQueue ( exception , false ) ;
529529 return QUIC_STATUS_SUCCESS ;
530530 }
531531 private unsafe int HandleEventShutdownInitiatedByPeer ( ref SHUTDOWN_INITIATED_BY_PEER_DATA data )
532532 {
533- CompleteAndDrainAcceptQueue ( ExceptionDispatchInfo . SetCurrentStackTrace ( ThrowHelper . GetConnectionAbortedException ( ( long ) data . ErrorCode ) ) ) ;
533+ CompleteAcceptQueue ( ExceptionDispatchInfo . SetCurrentStackTrace ( ThrowHelper . GetConnectionAbortedException ( ( long ) data . ErrorCode ) ) , false ) ;
534534 return QUIC_STATUS_SUCCESS ;
535535 }
536536 private unsafe int HandleEventShutdownComplete ( )
@@ -539,7 +539,7 @@ private unsafe int HandleEventShutdownComplete()
539539 _tlsSecret ? . WriteSecret ( ) ;
540540
541541 Exception exception = ExceptionDispatchInfo . SetCurrentStackTrace ( _disposed == 1 ? new ObjectDisposedException ( GetType ( ) . FullName ) : ThrowHelper . GetOperationAbortedException ( ) ) ;
542- CompleteAndDrainAcceptQueue ( exception ) ;
542+ CompleteAcceptQueue ( exception , true ) ;
543543 _connectedTcs . TrySetException ( exception ) ;
544544 _shutdownTokenSource . Cancel ( ) ;
545545 _shutdownTcs . TrySetResult ( final : true ) ;
@@ -643,22 +643,26 @@ private static unsafe int NativeCallback(QUIC_HANDLE* connection, void* context,
643643 }
644644 }
645645
646- private void CompleteAndDrainAcceptQueue ( Exception ? ex )
646+ private void CompleteAcceptQueue ( Exception ? ex , bool drain )
647647 {
648- if ( _acceptQueue . Writer . TryComplete ( ex ) )
648+ _acceptQueue . Writer . TryComplete ( ex ) ;
649+
650+ if ( drain )
649651 {
650- // also drain the queue. Because stream shutdown events are indicated before connection shutdown,
651- // the QuicStream instances have already been signaled and closed internally. We only need to dispose them,
652- // which in this situation should complete synchronously.
652+ // This should be only called after connection SHUTDOWN_COMPLETE has been indicated.
653+ // At that point, all streams have been already shut down internally and we need
654+ // only to close the handle via dispose, so DisposeAsync below should complete
655+ // synchronously (which is necessary for this method to be callable from MsQuic
656+ // event callback).
653657 while ( _acceptQueue . Reader . TryRead ( out QuicStream ? stream ) )
654658 {
655659 ValueTask task = stream . DisposeAsync ( ) ;
656660 Debug . Assert ( task . IsCompletedSuccessfully ) ;
657661 task . GetAwaiter ( ) . GetResult ( ) ;
658662 }
659- }
660663
661- Debug . Assert ( _acceptQueue . Reader . Completion . IsCompleted ) ;
664+ Debug . Assert ( _acceptQueue . Reader . Completion . IsCompleted ) ;
665+ }
662666 }
663667
664668 /// <summary>
@@ -709,6 +713,6 @@ public async ValueTask DisposeAsync()
709713 }
710714
711715 // Flush the queue and dispose all remaining streams.
712- CompleteAndDrainAcceptQueue ( ExceptionDispatchInfo . SetCurrentStackTrace ( new ObjectDisposedException ( GetType ( ) . FullName ) ) ) ;
716+ CompleteAcceptQueue ( ExceptionDispatchInfo . SetCurrentStackTrace ( new ObjectDisposedException ( GetType ( ) . FullName ) ) , true ) ;
713717 }
714718}
0 commit comments