42
42
using System . Collections . Generic ;
43
43
using System . Diagnostics ;
44
44
using System . IO ;
45
- using System . Runtime . CompilerServices ;
46
45
using System . Text ;
47
46
using System . Threading ;
48
47
using System . Threading . Tasks ;
48
+
49
49
using RabbitMQ . Client . Events ;
50
50
using RabbitMQ . Client . Exceptions ;
51
- using RabbitMQ . Client . Framing ;
52
51
using RabbitMQ . Client . Framing . Impl ;
53
52
using RabbitMQ . Util ;
54
53
@@ -71,19 +70,16 @@ abstract class ModelBase : IFullModel, IRecoverable
71
70
private readonly object _shutdownLock = new object ( ) ;
72
71
private readonly object _rpcLock = new object ( ) ;
73
72
private readonly object _confirmLock = new object ( ) ;
74
-
75
- private ulong _maxDeliveryId ;
76
- private ulong _deliveredItems = 0 ;
73
+ private readonly LinkedList < ulong > _pendingDeliveryTags = new LinkedList < ulong > ( ) ;
74
+ private readonly CountdownEvent _deliveryTagsCountdown = new CountdownEvent ( 0 ) ;
77
75
78
76
private EventHandler < ShutdownEventArgs > _modelShutdown ;
79
77
80
78
private bool _onlyAcksReceived = true ;
81
- private ulong _nextPublishSeqNo ;
82
79
83
80
public IConsumerDispatcher ConsumerDispatcher { get ; private set ; }
84
81
85
- public ModelBase ( ISession session )
86
- : this ( session , session . Connection . ConsumerWorkService )
82
+ public ModelBase ( ISession session ) : this ( session , session . Connection . ConsumerWorkService )
87
83
{ }
88
84
89
85
public ModelBase ( ISession session , ConsumerWorkService workService )
@@ -103,7 +99,7 @@ public ModelBase(ISession session, ConsumerWorkService workService)
103
99
protected void Initialise ( ISession session )
104
100
{
105
101
CloseReason = null ;
106
- _nextPublishSeqNo = 0 ;
102
+ NextPublishSeqNo = 0 ;
107
103
Session = session ;
108
104
Session . CommandReceived = HandleCommand ;
109
105
Session . SessionShutdown += OnSessionShutdown ;
@@ -180,7 +176,7 @@ public bool IsOpen
180
176
get { return CloseReason == null ; }
181
177
}
182
178
183
- public ulong NextPublishSeqNo { get => _nextPublishSeqNo ; }
179
+ public ulong NextPublishSeqNo { get ; private set ; }
184
180
185
181
public ISession Session { get ; private set ; }
186
182
@@ -494,11 +490,8 @@ public virtual void OnModelShutdown(ShutdownEventArgs reason)
494
490
}
495
491
}
496
492
}
497
- lock ( _confirmLock )
498
- {
499
- Monitor . Pulse ( _confirmLock ) ;
500
- }
501
493
494
+ _deliveryTagsCountdown . Reset ( 0 ) ;
502
495
_flowControlBlock . Set ( ) ;
503
496
}
504
497
@@ -1084,14 +1077,25 @@ public abstract void BasicNack(ulong deliveryTag,
1084
1077
bool multiple ,
1085
1078
bool requeue ) ;
1086
1079
1087
- internal void AllocatatePublishSeqNos ( int count )
1080
+ internal void AllocatePublishSeqNos ( int count )
1088
1081
{
1089
-
1090
- lock ( _confirmLock )
1082
+ if ( NextPublishSeqNo > 0 )
1091
1083
{
1092
- if ( _nextPublishSeqNo > 0 )
1084
+ lock ( _confirmLock )
1093
1085
{
1094
- _nextPublishSeqNo = InterlockedEx . Add ( ref _nextPublishSeqNo , ( ulong ) count ) ;
1086
+ if ( _deliveryTagsCountdown . IsSet )
1087
+ {
1088
+ _deliveryTagsCountdown . Reset ( count ) ;
1089
+ }
1090
+ else
1091
+ {
1092
+ _deliveryTagsCountdown . AddCount ( count ) ;
1093
+ }
1094
+
1095
+ for ( int i = 0 ; i < count ; i ++ )
1096
+ {
1097
+ _pendingDeliveryTags . AddLast ( NextPublishSeqNo ++ ) ;
1098
+ }
1095
1099
}
1096
1100
}
1097
1101
}
@@ -1111,11 +1115,21 @@ public void BasicPublish(string exchange,
1111
1115
{
1112
1116
basicProperties = CreateBasicProperties ( ) ;
1113
1117
}
1114
- if ( _nextPublishSeqNo > 0 )
1118
+
1119
+ if ( NextPublishSeqNo > 0 )
1115
1120
{
1116
1121
lock ( _confirmLock )
1117
1122
{
1118
- _nextPublishSeqNo = InterlockedEx . Increment ( ref _nextPublishSeqNo ) ;
1123
+ if ( _deliveryTagsCountdown . IsSet )
1124
+ {
1125
+ _deliveryTagsCountdown . Reset ( 1 ) ;
1126
+ }
1127
+ else
1128
+ {
1129
+ _deliveryTagsCountdown . AddCount ( ) ;
1130
+ }
1131
+
1132
+ _pendingDeliveryTags . AddLast ( NextPublishSeqNo ++ ) ;
1119
1133
}
1120
1134
}
1121
1135
@@ -1176,7 +1190,7 @@ public void ConfirmSelect()
1176
1190
{
1177
1191
if ( NextPublishSeqNo == 0UL )
1178
1192
{
1179
- _nextPublishSeqNo = 1 ;
1193
+ NextPublishSeqNo = 1 ;
1180
1194
}
1181
1195
1182
1196
_Private_ConfirmSelect ( false ) ;
@@ -1338,34 +1352,32 @@ public bool WaitForConfirms(TimeSpan timeout, out bool timedOut)
1338
1352
}
1339
1353
bool isWaitInfinite = timeout . TotalMilliseconds == Timeout . Infinite ;
1340
1354
Stopwatch stopwatch = Stopwatch . StartNew ( ) ;
1341
- lock ( _confirmLock )
1355
+ while ( true )
1342
1356
{
1343
- while ( true )
1357
+ if ( ! IsOpen )
1344
1358
{
1345
- if ( ! IsOpen )
1346
- {
1347
- throw new AlreadyClosedException ( CloseReason ) ;
1348
- }
1359
+ throw new AlreadyClosedException ( CloseReason ) ;
1360
+ }
1349
1361
1350
- if ( _deliveredItems == _nextPublishSeqNo - 1 )
1351
- {
1352
- bool aux = _onlyAcksReceived ;
1353
- _onlyAcksReceived = true ;
1354
- timedOut = false ;
1355
- return aux ;
1356
- }
1357
- if ( isWaitInfinite )
1358
- {
1359
- Monitor . Wait ( _confirmLock ) ;
1360
- }
1361
- else
1362
+ if ( _deliveryTagsCountdown . IsSet )
1363
+ {
1364
+ bool aux = _onlyAcksReceived ;
1365
+ _onlyAcksReceived = true ;
1366
+ timedOut = false ;
1367
+ return aux ;
1368
+ }
1369
+
1370
+ if ( isWaitInfinite )
1371
+ {
1372
+ _deliveryTagsCountdown . Wait ( ) ;
1373
+ }
1374
+ else
1375
+ {
1376
+ TimeSpan elapsed = stopwatch . Elapsed ;
1377
+ if ( elapsed > timeout || ! _deliveryTagsCountdown . Wait ( timeout - elapsed ) )
1362
1378
{
1363
- TimeSpan elapsed = stopwatch . Elapsed ;
1364
- if ( elapsed > timeout || ! Monitor . Wait ( _confirmLock , timeout - elapsed ) )
1365
- {
1366
- timedOut = true ;
1367
- return _onlyAcksReceived ;
1368
- }
1379
+ timedOut = true ;
1380
+ return _onlyAcksReceived ;
1369
1381
}
1370
1382
}
1371
1383
}
@@ -1411,33 +1423,41 @@ public void WaitForConfirmsOrDie(TimeSpan timeout)
1411
1423
internal void SendCommands ( IList < Command > commands )
1412
1424
{
1413
1425
_flowControlBlock . Wait ( ) ;
1414
- AllocatatePublishSeqNos ( commands . Count ) ;
1426
+ AllocatePublishSeqNos ( commands . Count ) ;
1415
1427
Session . Transmit ( commands ) ;
1416
1428
}
1417
1429
1418
1430
protected virtual void handleAckNack ( ulong deliveryTag , bool multiple , bool isNack )
1419
1431
{
1420
- lock ( _confirmLock )
1432
+ // No need to do this if publisher confirms have never been enabled.
1433
+ if ( NextPublishSeqNo > 0 )
1421
1434
{
1422
- _deliveredItems = InterlockedEx . Increment ( ref _deliveredItems ) ;
1423
-
1424
- if ( multiple && _maxDeliveryId < deliveryTag )
1435
+ // let's take a lock so we can assume that deliveryTags are unique, never duplicated and always sorted
1436
+ lock ( _confirmLock )
1425
1437
{
1426
- _maxDeliveryId = deliveryTag ;
1427
- }
1438
+ // No need to do anything if there are no delivery tags in the list
1439
+ if ( _pendingDeliveryTags . Count > 0 )
1440
+ {
1441
+ if ( multiple )
1442
+ {
1443
+ while ( _pendingDeliveryTags . First . Value < deliveryTag )
1444
+ {
1445
+ _pendingDeliveryTags . RemoveFirst ( ) ;
1446
+ _deliveryTagsCountdown . Signal ( ) ;
1447
+ }
1448
+ }
1428
1449
1429
- _deliveredItems = Math . Max ( _maxDeliveryId , _deliveredItems ) ;
1430
- _onlyAcksReceived = _onlyAcksReceived && ! isNack ;
1431
- if ( _deliveredItems == _nextPublishSeqNo - 1 )
1432
- {
1433
- Monitor . Pulse ( _confirmLock ) ;
1450
+ if ( _pendingDeliveryTags . Remove ( deliveryTag ) )
1451
+ {
1452
+ _deliveryTagsCountdown . Signal ( ) ;
1453
+ }
1454
+ }
1455
+
1456
+ _onlyAcksReceived = _onlyAcksReceived && ! isNack ;
1434
1457
}
1435
1458
}
1436
-
1437
1459
}
1438
1460
1439
-
1440
-
1441
1461
private QueueDeclareOk QueueDeclare ( string queue , bool passive , bool durable , bool exclusive ,
1442
1462
bool autoDelete , IDictionary < string , object > arguments )
1443
1463
{
@@ -1479,26 +1499,5 @@ public class QueueDeclareRpcContinuation : SimpleBlockingRpcContinuation
1479
1499
{
1480
1500
public QueueDeclareOk m_result ;
1481
1501
}
1482
-
1483
- public static class InterlockedEx
1484
- {
1485
- public static ulong Increment ( ref ulong location )
1486
- {
1487
- long incrementedSigned = Interlocked . Increment ( ref Unsafe . As < ulong , long > ( ref location ) ) ;
1488
- return Unsafe . As < long , ulong > ( ref incrementedSigned ) ;
1489
- }
1490
-
1491
- public static ulong Decrement ( ref ulong location )
1492
- {
1493
- long decrementedSigned = Interlocked . Decrement ( ref Unsafe . As < ulong , long > ( ref location ) ) ;
1494
- return Unsafe . As < long , ulong > ( ref decrementedSigned ) ;
1495
- }
1496
-
1497
- public static ulong Add ( ref ulong location , ulong value )
1498
- {
1499
- long addSigned = Interlocked . Add ( ref Unsafe . As < ulong , long > ( ref location ) , Unsafe . As < ulong , long > ( ref value ) ) ;
1500
- return Unsafe . As < long , ulong > ( ref addSigned ) ;
1501
- }
1502
- }
1503
1502
}
1504
1503
}
0 commit comments