@@ -525,12 +525,12 @@ private unsafe int HandleEventShutdownInitiatedByTransport(ref SHUTDOWN_INITIATE
525
525
{
526
526
Exception exception = ExceptionDispatchInfo . SetCurrentStackTrace ( ThrowHelper . GetExceptionForMsQuicStatus ( data . Status , ( long ) data . ErrorCode ) ) ;
527
527
_connectedTcs . TrySetException ( exception ) ;
528
- CompleteAndDrainAcceptQueue ( exception ) ;
528
+ CompleteAcceptQueue ( exception , false ) ;
529
529
return QUIC_STATUS_SUCCESS ;
530
530
}
531
531
private unsafe int HandleEventShutdownInitiatedByPeer ( ref SHUTDOWN_INITIATED_BY_PEER_DATA data )
532
532
{
533
- CompleteAndDrainAcceptQueue ( ExceptionDispatchInfo . SetCurrentStackTrace ( ThrowHelper . GetConnectionAbortedException ( ( long ) data . ErrorCode ) ) ) ;
533
+ CompleteAcceptQueue ( ExceptionDispatchInfo . SetCurrentStackTrace ( ThrowHelper . GetConnectionAbortedException ( ( long ) data . ErrorCode ) ) , false ) ;
534
534
return QUIC_STATUS_SUCCESS ;
535
535
}
536
536
private unsafe int HandleEventShutdownComplete ( )
@@ -539,7 +539,7 @@ private unsafe int HandleEventShutdownComplete()
539
539
_tlsSecret ? . WriteSecret ( ) ;
540
540
541
541
Exception exception = ExceptionDispatchInfo . SetCurrentStackTrace ( _disposed == 1 ? new ObjectDisposedException ( GetType ( ) . FullName ) : ThrowHelper . GetOperationAbortedException ( ) ) ;
542
- CompleteAndDrainAcceptQueue ( exception ) ;
542
+ CompleteAcceptQueue ( exception , true ) ;
543
543
_connectedTcs . TrySetException ( exception ) ;
544
544
_shutdownTokenSource . Cancel ( ) ;
545
545
_shutdownTcs . TrySetResult ( final : true ) ;
@@ -643,22 +643,26 @@ private static unsafe int NativeCallback(QUIC_HANDLE* connection, void* context,
643
643
}
644
644
}
645
645
646
- private void CompleteAndDrainAcceptQueue ( Exception ? ex )
646
+ private void CompleteAcceptQueue ( Exception ? ex , bool drain )
647
647
{
648
- if ( _acceptQueue . Writer . TryComplete ( ex ) )
648
+ _acceptQueue . Writer . TryComplete ( ex ) ;
649
+
650
+ if ( drain )
649
651
{
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).
653
657
while ( _acceptQueue . Reader . TryRead ( out QuicStream ? stream ) )
654
658
{
655
659
ValueTask task = stream . DisposeAsync ( ) ;
656
660
Debug . Assert ( task . IsCompletedSuccessfully ) ;
657
661
task . GetAwaiter ( ) . GetResult ( ) ;
658
662
}
659
- }
660
663
661
- Debug . Assert ( _acceptQueue . Reader . Completion . IsCompleted ) ;
664
+ Debug . Assert ( _acceptQueue . Reader . Completion . IsCompleted ) ;
665
+ }
662
666
}
663
667
664
668
/// <summary>
@@ -709,6 +713,6 @@ public async ValueTask DisposeAsync()
709
713
}
710
714
711
715
// 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 ) ;
713
717
}
714
718
}
0 commit comments