@@ -379,19 +379,36 @@ public void UpdateMaxFrameSize(uint maxFrameSize)
379
379
}
380
380
}
381
381
382
+ /// <summary>
383
+ /// Call while in the <see cref="_writeLock"/>.
384
+ /// </summary>
385
+ /// <returns><c>true</c> if already completed.</returns>
386
+ private bool CompleteUnsynchronized ( )
387
+ {
388
+ if ( _completed )
389
+ {
390
+ return true ;
391
+ }
392
+
393
+ _completed = true ;
394
+ _outputWriter . Abort ( ) ;
395
+
396
+ return false ;
397
+ }
398
+
382
399
public void Complete ( )
383
400
{
384
401
lock ( _writeLock )
385
402
{
386
- if ( _completed )
403
+ if ( CompleteUnsynchronized ( ) )
387
404
{
388
405
return ;
389
406
}
390
-
391
- _completed = true ;
392
- AbortConnectionFlowControl ( ) ;
393
- _outputWriter . Abort ( ) ;
394
407
}
408
+
409
+ // Call outside of _writeLock as this can call Http2OutputProducer.Stop which can acquire Http2OutputProducer._dataWriterLock
410
+ // which is not the desired lock order
411
+ AbortConnectionFlowControl ( ) ;
395
412
}
396
413
397
414
public Task ShutdownAsync ( )
@@ -413,8 +430,15 @@ public void Abort(ConnectionAbortedException error)
413
430
_aborted = true ;
414
431
_connectionContext . Abort ( error ) ;
415
432
416
- Complete ( ) ;
433
+ if ( CompleteUnsynchronized ( ) )
434
+ {
435
+ return ;
436
+ }
417
437
}
438
+
439
+ // Call outside of _writeLock as this can call Http2OutputProducer.Stop which can acquire Http2OutputProducer._dataWriterLock
440
+ // which is not the desired lock order
441
+ AbortConnectionFlowControl ( ) ;
418
442
}
419
443
420
444
private ValueTask < FlushResult > FlushEndOfStreamHeadersAsync ( Http2Stream stream )
@@ -487,7 +511,7 @@ private void WriteResponseHeadersUnsynchronized(int streamId, int statusCode, Ht
487
511
_outgoingFrame . PrepareHeaders ( headerFrameFlags , streamId ) ;
488
512
var buffer = _headerEncodingBuffer . AsSpan ( ) ;
489
513
var done = HPackHeaderWriter . BeginEncodeHeaders ( statusCode , _hpackEncoder , _headersEnumerator , buffer , out var payloadLength ) ;
490
- FinishWritingHeaders ( streamId , payloadLength , done ) ;
514
+ FinishWritingHeadersUnsynchronized ( streamId , payloadLength , done ) ;
491
515
}
492
516
// Any exception from the HPack encoder can leave the dynamic table in a corrupt state.
493
517
// Since we allow custom header encoders we don't know what type of exceptions to expect.
@@ -528,7 +552,7 @@ private ValueTask<FlushResult> WriteDataAndTrailersAsync(Http2Stream stream, in
528
552
_outgoingFrame . PrepareHeaders ( Http2HeadersFrameFlags . END_STREAM , streamId ) ;
529
553
var buffer = _headerEncodingBuffer . AsSpan ( ) ;
530
554
var done = HPackHeaderWriter . BeginEncodeHeaders ( _hpackEncoder , _headersEnumerator , buffer , out var payloadLength ) ;
531
- FinishWritingHeaders ( streamId , payloadLength , done ) ;
555
+ FinishWritingHeadersUnsynchronized ( streamId , payloadLength , done ) ;
532
556
}
533
557
// Any exception from the HPack encoder can leave the dynamic table in a corrupt state.
534
558
// Since we allow custom header encoders we don't know what type of exceptions to expect.
@@ -542,7 +566,7 @@ private ValueTask<FlushResult> WriteDataAndTrailersAsync(Http2Stream stream, in
542
566
}
543
567
}
544
568
545
- private void FinishWritingHeaders ( int streamId , int payloadLength , bool done )
569
+ private void FinishWritingHeadersUnsynchronized ( int streamId , int payloadLength , bool done )
546
570
{
547
571
var buffer = _headerEncodingBuffer . AsSpan ( ) ;
548
572
_outgoingFrame . PayloadLength = payloadLength ;
@@ -934,6 +958,11 @@ private void ConsumeConnectionWindow(long bytes)
934
958
}
935
959
}
936
960
961
+ /// <summary>
962
+ /// Do not call this method under the _writeLock.
963
+ /// This method can call Http2OutputProducer.Stop which can acquire Http2OutputProducer._dataWriterLock
964
+ /// which is not the desired lock order
965
+ /// </summary>
937
966
private void AbortConnectionFlowControl ( )
938
967
{
939
968
lock ( _windowUpdateLock )
0 commit comments