@@ -75,6 +75,10 @@ public abstract class ModelBase : IFullModel, IRecoverable
75
75
76
76
//private readonly SynchronizedList<ulong> m_unconfirmedSet = new SynchronizedList<ulong>();
77
77
private readonly ConcurrentDictionary < ulong , bool > m_unconfirmedSet = new ConcurrentDictionary < ulong , bool > ( ) ;
78
+ private ulong _highestDeliveryTag = 0 ;
79
+ private Task _multipleConfirmCleanup ;
80
+ private SemaphoreSlim _multipleConfirmLock = new SemaphoreSlim ( 1 , 1 ) ;
81
+ private readonly CancellationTokenSource _connectionClosingCancellation = new CancellationTokenSource ( ) ;
78
82
private readonly SemaphoreSlim _outstandingSemaphore = new SemaphoreSlim ( 0 ) ;
79
83
80
84
private EventHandler < EventArgs > m_basicRecoverOk ;
@@ -114,6 +118,7 @@ protected void Initialise(ISession session)
114
118
Session = session ;
115
119
Session . CommandReceived = HandleCommand ;
116
120
Session . SessionShutdown += OnSessionShutdown ;
121
+ _multipleConfirmCleanup = Task . Run ( CleanupUnconfirmedTagsAsync ) ;
117
122
}
118
123
119
124
public TimeSpan HandshakeContinuationTimeout
@@ -268,6 +273,7 @@ public void Close(ShutdownEventArgs reason, bool abort)
268
273
{
269
274
var k = new ShutdownContinuation ( ) ;
270
275
ModelShutdown += k . OnConnectionShutdown ;
276
+ _connectionClosingCancellation . Cancel ( ) ;
271
277
272
278
try
273
279
{
@@ -278,6 +284,7 @@ public void Close(ShutdownEventArgs reason, bool abort)
278
284
}
279
285
k . Wait ( TimeSpan . FromMilliseconds ( 10000 ) ) ;
280
286
ConsumerDispatcher . Shutdown ( this ) ;
287
+ _multipleConfirmCleanup . Wait ( ) ;
281
288
}
282
289
catch ( AlreadyClosedException )
283
290
{
@@ -422,6 +429,13 @@ public MethodBase ModelRpc(MethodBase method, ContentHeaderBase header, byte[] b
422
429
}
423
430
}
424
431
432
+ public async ValueTask < MethodBase > ModelRpcAsync ( MethodBase method , ContentHeaderBase header , byte [ ] body )
433
+ {
434
+ var k = new AsyncRpcContinuation ( ) ;
435
+ TransmitAndEnqueue ( new Command ( method , header , body ) , k ) ;
436
+ return ( await k . GetReplyAsync ( this . ContinuationTimeout ) . ConfigureAwait ( false ) ) . Method ;
437
+ }
438
+
425
439
public void ModelSend ( MethodBase method , ContentHeaderBase header , byte [ ] body )
426
440
{
427
441
if ( method . HasContent )
@@ -1574,23 +1588,63 @@ protected virtual void handleAckNack(ulong deliveryTag, bool multiple, bool isNa
1574
1588
{
1575
1589
if ( multiple )
1576
1590
{
1577
- foreach ( ulong key in m_unconfirmedSet . Keys )
1591
+ if ( _highestDeliveryTag < deliveryTag )
1578
1592
{
1579
- if ( key <= deliveryTag )
1593
+ // Multiple confirms, let's trigger a cleanup of any older deliveries.
1594
+ _highestDeliveryTag = deliveryTag ;
1595
+ try
1580
1596
{
1581
- m_unconfirmedSet . TryRemove ( key , out _ ) ;
1597
+ _multipleConfirmLock . Release ( ) ;
1598
+ }
1599
+ catch ( SemaphoreFullException )
1600
+ {
1601
+ // Ignore if we are trying to release often.
1582
1602
}
1583
1603
}
1584
1604
}
1585
- else
1586
- {
1587
- m_unconfirmedSet . TryRemove ( deliveryTag , out _ ) ;
1588
- }
1605
+
1606
+ m_unconfirmedSet . TryRemove ( deliveryTag , out _ ) ;
1589
1607
1590
1608
m_onlyAcksReceived = m_onlyAcksReceived && ! isNack ;
1609
+ TriggerAllOutstandingCompleted ( ) ;
1610
+ }
1611
+
1612
+ private void TriggerAllOutstandingCompleted ( )
1613
+ {
1591
1614
if ( m_unconfirmedSet . Count == 0 )
1592
1615
{
1593
- _outstandingSemaphore . Release ( ) ;
1616
+ try
1617
+ {
1618
+ _outstandingSemaphore . Release ( ) ;
1619
+ }
1620
+ catch ( SemaphoreFullException )
1621
+ {
1622
+ // Swallow the semaphore full exception.
1623
+ }
1624
+ }
1625
+ }
1626
+
1627
+ public async Task CleanupUnconfirmedTagsAsync ( )
1628
+ {
1629
+ while ( ! _connectionClosingCancellation . IsCancellationRequested )
1630
+ {
1631
+ try
1632
+ {
1633
+ await _multipleConfirmLock . WaitAsync ( _connectionClosingCancellation . Token ) . ConfigureAwait ( false ) ;
1634
+ foreach ( ulong key in m_unconfirmedSet . Keys )
1635
+ {
1636
+ if ( key <= _highestDeliveryTag )
1637
+ {
1638
+ m_unconfirmedSet . TryRemove ( key , out _ ) ;
1639
+ }
1640
+ }
1641
+
1642
+ TriggerAllOutstandingCompleted ( ) ;
1643
+ }
1644
+ catch ( TaskCanceledException )
1645
+ {
1646
+ // Swallow the task cancel exception since the model is being closed.
1647
+ }
1594
1648
}
1595
1649
}
1596
1650
0 commit comments