@@ -370,19 +370,36 @@ public void UpdateMaxFrameSize(uint maxFrameSize)
370
370
}
371
371
}
372
372
373
+ /// <summary>
374
+ /// Call while in the <see cref="_writeLock"/>.
375
+ /// </summary>
376
+ /// <returns><c>true</c> if already completed.</returns>
377
+ private bool CompleteUnsynchronized ( )
378
+ {
379
+ if ( _completed )
380
+ {
381
+ return true ;
382
+ }
383
+
384
+ _completed = true ;
385
+ _outputWriter . Abort ( ) ;
386
+
387
+ return false ;
388
+ }
389
+
373
390
public void Complete ( )
374
391
{
375
392
lock ( _writeLock )
376
393
{
377
- if ( _completed )
394
+ if ( CompleteUnsynchronized ( ) )
378
395
{
379
396
return ;
380
397
}
381
-
382
- _completed = true ;
383
- AbortConnectionFlowControl ( ) ;
384
- _outputWriter . Abort ( ) ;
385
398
}
399
+
400
+ // Call outside of _writeLock as this can call Http2OutputProducer.Stop which can acquire Http2OutputProducer._dataWriterLock
401
+ // which is not the desired lock order
402
+ AbortConnectionFlowControl ( ) ;
386
403
}
387
404
388
405
public Task ShutdownAsync ( )
@@ -404,8 +421,15 @@ public void Abort(ConnectionAbortedException error)
404
421
_aborted = true ;
405
422
_connectionContext . Abort ( error ) ;
406
423
407
- Complete ( ) ;
424
+ if ( CompleteUnsynchronized ( ) )
425
+ {
426
+ return ;
427
+ }
408
428
}
429
+
430
+ // Call outside of _writeLock as this can call Http2OutputProducer.Stop which can acquire Http2OutputProducer._dataWriterLock
431
+ // which is not the desired lock order
432
+ AbortConnectionFlowControl ( ) ;
409
433
}
410
434
411
435
private ValueTask < FlushResult > FlushEndOfStreamHeadersAsync ( Http2Stream stream )
@@ -478,7 +502,7 @@ private void WriteResponseHeadersUnsynchronized(int streamId, int statusCode, Ht
478
502
_outgoingFrame . PrepareHeaders ( headerFrameFlags , streamId ) ;
479
503
var buffer = _headerEncodingBuffer . AsSpan ( ) ;
480
504
var done = HPackHeaderWriter . BeginEncodeHeaders ( statusCode , _hpackEncoder , _headersEnumerator , buffer , out var payloadLength ) ;
481
- FinishWritingHeaders ( streamId , payloadLength , done ) ;
505
+ FinishWritingHeadersUnsynchronized ( streamId , payloadLength , done ) ;
482
506
}
483
507
// Any exception from the HPack encoder can leave the dynamic table in a corrupt state.
484
508
// Since we allow custom header encoders we don't know what type of exceptions to expect.
@@ -519,7 +543,7 @@ private ValueTask<FlushResult> WriteDataAndTrailersAsync(Http2Stream stream, in
519
543
_outgoingFrame . PrepareHeaders ( Http2HeadersFrameFlags . END_STREAM , streamId ) ;
520
544
var buffer = _headerEncodingBuffer . AsSpan ( ) ;
521
545
var done = HPackHeaderWriter . BeginEncodeHeaders ( _hpackEncoder , _headersEnumerator , buffer , out var payloadLength ) ;
522
- FinishWritingHeaders ( streamId , payloadLength , done ) ;
546
+ FinishWritingHeadersUnsynchronized ( streamId , payloadLength , done ) ;
523
547
}
524
548
// Any exception from the HPack encoder can leave the dynamic table in a corrupt state.
525
549
// Since we allow custom header encoders we don't know what type of exceptions to expect.
@@ -533,7 +557,7 @@ private ValueTask<FlushResult> WriteDataAndTrailersAsync(Http2Stream stream, in
533
557
}
534
558
}
535
559
536
- private void FinishWritingHeaders ( int streamId , int payloadLength , bool done )
560
+ private void FinishWritingHeadersUnsynchronized ( int streamId , int payloadLength , bool done )
537
561
{
538
562
var buffer = _headerEncodingBuffer . AsSpan ( ) ;
539
563
_outgoingFrame . PayloadLength = payloadLength ;
@@ -925,6 +949,11 @@ private void ConsumeConnectionWindow(long bytes)
925
949
}
926
950
}
927
951
952
+ /// <summary>
953
+ /// Do not call this method under the _writeLock.
954
+ /// This method can call Http2OutputProducer.Stop which can acquire Http2OutputProducer._dataWriterLock
955
+ /// which is not the desired lock order
956
+ /// </summary>
928
957
private void AbortConnectionFlowControl ( )
929
958
{
930
959
lock ( _windowUpdateLock )
0 commit comments