33
33
using System . Buffers ;
34
34
using System . Collections . Generic ;
35
35
using System . IO ;
36
+ using System . IO . Pipelines ;
36
37
using System . Net ;
37
38
using System . Net . Sockets ;
38
39
using System . Reflection ;
@@ -153,31 +154,35 @@ public event EventHandler<ShutdownEventArgs> ConnectionShutdown
153
154
/// <summary>
154
155
/// This event is never fired by non-recovering connections but it is a part of the <see cref="IConnection"/> interface.
155
156
/// </summary>
156
- public event EventHandler < EventArgs > RecoverySucceeded {
157
+ public event EventHandler < EventArgs > RecoverySucceeded
158
+ {
157
159
add { }
158
160
remove { }
159
161
}
160
162
161
163
/// <summary>
162
164
/// This event is never fired by non-recovering connections but it is a part of the <see cref="IConnection"/> interface.
163
165
/// </summary>
164
- public event EventHandler < ConnectionRecoveryErrorEventArgs > ConnectionRecoveryError {
166
+ public event EventHandler < ConnectionRecoveryErrorEventArgs > ConnectionRecoveryError
167
+ {
165
168
add { }
166
169
remove { }
167
170
}
168
171
169
172
/// <summary>
170
173
/// This event is never fired by non-recovering connections but it is a part of the <see cref="IConnection"/> interface.
171
174
/// </summary>
172
- public event EventHandler < ConsumerTagChangedAfterRecoveryEventArgs > ConsumerTagChangeAfterRecovery {
175
+ public event EventHandler < ConsumerTagChangedAfterRecoveryEventArgs > ConsumerTagChangeAfterRecovery
176
+ {
173
177
add { }
174
178
remove { }
175
179
}
176
180
177
181
/// <summary>
178
182
/// This event is never fired by non-recovering connections but it is a part of the <see cref="IConnection"/> interface.
179
183
/// </summary>
180
- public event EventHandler < QueueNameChangedAfterRecoveryEventArgs > QueueNameChangeAfterRecovery {
184
+ public event EventHandler < QueueNameChangedAfterRecoveryEventArgs > QueueNameChangeAfterRecovery
185
+ {
181
186
add { }
182
187
remove { }
183
188
}
@@ -332,15 +337,35 @@ public void Close(ShutdownEventArgs reason, bool abort, TimeSpan timeout)
332
337
///<remarks>
333
338
/// Loop only used while quiescing. Use only to cleanly close connection
334
339
///</remarks>
335
- public void ClosingLoop ( )
340
+ public async ValueTask ClosingLoop ( )
336
341
{
337
342
try
338
343
{
339
344
_frameHandler . ReadTimeout = TimeSpan . Zero ;
340
345
// Wait for response/socket closure or timeout
346
+ bool allowSync = true ;
341
347
while ( ! _closed )
342
348
{
343
- MainLoopIteration ( ) ;
349
+ // Let's read some bytes
350
+ if ( ! ( allowSync && _frameHandler . PipeReader . TryRead ( out ReadResult readResult ) ) )
351
+ {
352
+ readResult = await _frameHandler . PipeReader . ReadAsync ( ) . ConfigureAwait ( false ) ;
353
+ }
354
+
355
+ int handled = 0 ;
356
+ ReadOnlySequence < byte > buffer = readResult . Buffer ;
357
+ if ( ! buffer . IsEmpty )
358
+ {
359
+ handled = MainLoopIteration ( ref buffer ) ;
360
+ }
361
+
362
+ allowSync = handled != 0 ;
363
+ _frameHandler . PipeReader . AdvanceTo ( buffer . Start , buffer . End ) ;
364
+
365
+ if ( handled == 0 && readResult . IsCompleted )
366
+ {
367
+ throw new EndOfStreamException ( ) ;
368
+ }
344
369
}
345
370
}
346
371
catch ( ObjectDisposedException ode )
@@ -472,18 +497,38 @@ public void LogCloseError(string error, Exception ex)
472
497
ShutdownReport . Add ( new ShutdownReportEntry ( error , ex ) ) ;
473
498
}
474
499
475
- public void MainLoop ( )
500
+ public async Task MainLoop ( )
476
501
{
477
502
try
478
503
{
479
504
bool shutdownCleanly = false ;
480
505
try
481
506
{
507
+ bool allowSync = true ;
482
508
while ( _running )
483
509
{
484
510
try
485
511
{
486
- MainLoopIteration ( ) ;
512
+ // Let's read some bytes
513
+ if ( ! ( allowSync && _frameHandler . PipeReader . TryRead ( out ReadResult readResult ) ) )
514
+ {
515
+ readResult = await _frameHandler . PipeReader . ReadAsync ( ) . ConfigureAwait ( false ) ;
516
+ }
517
+
518
+ int handled = 0 ;
519
+ ReadOnlySequence < byte > buffer = readResult . Buffer ;
520
+ if ( ! buffer . IsEmpty )
521
+ {
522
+ handled = MainLoopIteration ( ref buffer ) ;
523
+ }
524
+
525
+ allowSync = handled != 0 ;
526
+ _frameHandler . PipeReader . AdvanceTo ( buffer . Start , buffer . End ) ;
527
+
528
+ if ( handled == 0 && readResult . IsCompleted )
529
+ {
530
+ throw new EndOfStreamException ( ) ;
531
+ }
487
532
}
488
533
catch ( SoftProtocolException spe )
489
534
{
@@ -520,7 +565,7 @@ public void MainLoop()
520
565
#pragma warning disable 0168
521
566
try
522
567
{
523
- ClosingLoop ( ) ;
568
+ await ClosingLoop ( ) . ConfigureAwait ( false ) ;
524
569
}
525
570
catch ( SocketException )
526
571
{
@@ -539,52 +584,59 @@ public void MainLoop()
539
584
}
540
585
}
541
586
542
- public void MainLoopIteration ( )
587
+ public int MainLoopIteration ( ref ReadOnlySequence < byte > buffer )
543
588
{
544
- InboundFrame frame = _frameHandler . ReadFrame ( ) ;
545
- NotifyHeartbeatListener ( ) ;
546
-
547
- bool shallReturn = true ;
548
- // We have received an actual frame.
549
- if ( frame . Type == FrameType . FrameHeartbeat )
589
+ int handled = 0 ;
590
+ while ( InboundFrame . TryReadFrame ( ref buffer , out InboundFrame frame ) )
550
591
{
551
- // Ignore it: we've already just reset the heartbeat
552
- }
553
- else if ( frame . Channel == 0 )
554
- {
555
- // In theory, we could get non-connection.close-ok
556
- // frames here while we're quiescing (m_closeReason !=
557
- // null). In practice, there's a limited number of
558
- // things the server can ask of us on channel 0 -
559
- // essentially, just connection.close. That, combined
560
- // with the restrictions on pipelining, mean that
561
- // we're OK here to handle channel 0 traffic in a
562
- // quiescing situation, even though technically we
563
- // should be ignoring everything except
564
- // connection.close-ok.
565
- shallReturn = _session0 . HandleFrame ( in frame ) ;
566
- }
567
- else
568
- {
569
- // If we're still m_running, but have a m_closeReason,
570
- // then we must be quiescing, which means any inbound
571
- // frames for non-zero channels (and any inbound
572
- // commands on channel zero that aren't
573
- // Connection.CloseOk) must be discarded.
574
- if ( _closeReason is null )
592
+ NotifyHeartbeatListener ( ) ;
593
+
594
+ bool shallReturn = true ;
595
+ // We have received an actual frame.
596
+ if ( frame . Type == FrameType . FrameHeartbeat )
575
597
{
576
- // No close reason, not quiescing the
577
- // connection. Handle the frame. (Of course, the
578
- // Session itself may be quiescing this particular
579
- // channel, but that's none of our concern.)
580
- shallReturn = _sessionManager . Lookup ( frame . Channel ) . HandleFrame ( in frame ) ;
598
+ // Ignore it: we've already just reset the heartbeat
599
+ }
600
+ else if ( frame . Channel == 0 )
601
+ {
602
+ // In theory, we could get non-connection.close-ok
603
+ // frames here while we're quiescing (m_closeReason !=
604
+ // null). In practice, there's a limited number of
605
+ // things the server can ask of us on channel 0 -
606
+ // essentially, just connection.close. That, combined
607
+ // with the restrictions on pipelining, mean that
608
+ // we're OK here to handle channel 0 traffic in a
609
+ // quiescing situation, even though technically we
610
+ // should be ignoring everything except
611
+ // connection.close-ok.
612
+ shallReturn = _session0 . HandleFrame ( in frame ) ;
613
+ }
614
+ else
615
+ {
616
+ // If we're still m_running, but have a m_closeReason,
617
+ // then we must be quiescing, which means any inbound
618
+ // frames for non-zero channels (and any inbound
619
+ // commands on channel zero that aren't
620
+ // Connection.CloseOk) must be discarded.
621
+ if ( _closeReason is null )
622
+ {
623
+ // No close reason, not quiescing the
624
+ // connection. Handle the frame. (Of course, the
625
+ // Session itself may be quiescing this particular
626
+ // channel, but that's none of our concern.)
627
+ shallReturn = _sessionManager . Lookup ( frame . Channel ) . HandleFrame ( in frame ) ;
628
+ }
581
629
}
582
- }
583
630
584
- if ( shallReturn )
585
- {
586
- frame . ReturnPayload ( ) ;
631
+ if ( shallReturn )
632
+ {
633
+ frame . ReturnPayload ( ) ;
634
+ }
635
+
636
+ handled ++ ;
587
637
}
638
+
639
+ return handled ;
588
640
}
589
641
590
642
private void NotifyHeartbeatListener ( )
@@ -796,7 +848,7 @@ public void MaybeStartHeartbeatTimers()
796
848
797
849
public void StartMainLoop ( )
798
850
{
799
- _mainLoopTask = Task . Run ( ( Action ) MainLoop ) ;
851
+ _mainLoopTask = Task . Run ( MainLoop ) ;
800
852
}
801
853
802
854
public void HeartbeatReadTimerCallback ( object state )
@@ -871,7 +923,7 @@ public void HeartbeatWriteTimerCallback(object state)
871
923
{
872
924
if ( ! _closed )
873
925
{
874
- Write ( Client . Impl . Framing . Heartbeat . GetHeartbeatFrame ( ) ) ;
926
+ Write ( OutgoingFrame . CreateHeartbeat ( ) ) ;
875
927
_heartbeatWriteTimer ? . Change ( ( int ) _heartbeatTimeSpan . TotalMilliseconds , Timeout . Infinite ) ;
876
928
}
877
929
}
@@ -908,9 +960,9 @@ public override string ToString()
908
960
return string . Format ( "Connection({0},{1})" , _id , Endpoint ) ;
909
961
}
910
962
911
- public void Write ( ReadOnlyMemory < byte > memory )
963
+ public void Write ( OutgoingFrame outgoingFrame )
912
964
{
913
- _frameHandler . Write ( memory ) ;
965
+ _frameHandler . Write ( outgoingFrame ) ;
914
966
}
915
967
916
968
public void UpdateSecret ( string newSecret , string reason )
0 commit comments