@@ -60,9 +60,9 @@ internal abstract class ChannelBase : IChannel, IRecoverable
60
60
private bool _publisherConfirmationsEnabled = false ;
61
61
private bool _publisherConfirmationTrackingEnabled = false ;
62
62
private ulong _nextPublishSeqNo = 0 ;
63
- private SemaphoreSlim _confirmSemaphore = new ( 1 , 1 ) ;
64
- private LinkedList < ulong > _pendingDeliveryTags = new ( ) ;
65
- private List < TaskCompletionSource < bool > > _confirmsTaskCompletionSources = new ( ) ;
63
+ private readonly SemaphoreSlim _confirmSemaphore = new ( 1 , 1 ) ;
64
+ private readonly LinkedList < ulong > _pendingDeliveryTags = new ( ) ;
65
+ private readonly Dictionary < ulong , TaskCompletionSource < bool > > _confirmsTaskCompletionSources = new ( ) ;
66
66
67
67
private bool _onlyAcksReceived = true ;
68
68
@@ -508,7 +508,7 @@ await _confirmSemaphore.WaitAsync(reason.CancellationToken)
508
508
if ( _confirmsTaskCompletionSources ? . Count > 0 )
509
509
{
510
510
var exception = new AlreadyClosedException ( reason ) ;
511
- foreach ( TaskCompletionSource < bool > confirmsTaskCompletionSource in _confirmsTaskCompletionSources )
511
+ foreach ( TaskCompletionSource < bool > confirmsTaskCompletionSource in _confirmsTaskCompletionSources . Values )
512
512
{
513
513
confirmsTaskCompletionSource . TrySetException ( exception ) ;
514
514
}
@@ -983,6 +983,7 @@ public async ValueTask BasicPublishAsync<TProperties>(string exchange, string ro
983
983
CancellationToken cancellationToken = default )
984
984
where TProperties : IReadOnlyBasicProperties , IAmqpHeader
985
985
{
986
+ TaskCompletionSource < bool > ? publisherConfirmationTcs = null ;
986
987
if ( _publisherConfirmationsEnabled )
987
988
{
988
989
await _confirmSemaphore . WaitAsync ( cancellationToken )
@@ -991,11 +992,9 @@ await _confirmSemaphore.WaitAsync(cancellationToken)
991
992
{
992
993
if ( _publisherConfirmationTrackingEnabled )
993
994
{
994
- if ( _pendingDeliveryTags is null )
995
- {
996
- throw new InvalidOperationException ( InternalConstants . BugFound ) ;
997
- }
998
995
_pendingDeliveryTags . AddLast ( _nextPublishSeqNo ) ;
996
+ publisherConfirmationTcs = new ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
997
+ _confirmsTaskCompletionSources [ _nextPublishSeqNo ] = publisherConfirmationTcs ;
999
998
}
1000
999
1001
1000
_nextPublishSeqNo ++ ;
@@ -1039,7 +1038,7 @@ await ModelSendAsync(in cmd, in basicProperties, body, cancellationToken)
1039
1038
. ConfigureAwait ( false ) ;
1040
1039
}
1041
1040
}
1042
- catch
1041
+ catch ( Exception ex )
1043
1042
{
1044
1043
if ( _publisherConfirmationsEnabled )
1045
1044
{
@@ -1059,7 +1058,21 @@ await _confirmSemaphore.WaitAsync(cancellationToken)
1059
1058
}
1060
1059
}
1061
1060
1062
- throw ;
1061
+ if ( publisherConfirmationTcs is not null )
1062
+ {
1063
+ publisherConfirmationTcs . SetException ( ex ) ;
1064
+ }
1065
+ else
1066
+ {
1067
+ throw ;
1068
+ }
1069
+ }
1070
+
1071
+ if ( publisherConfirmationTcs is not null )
1072
+ {
1073
+ // TODO timeout?
1074
+ await publisherConfirmationTcs . Task
1075
+ . ConfigureAwait ( false ) ;
1063
1076
}
1064
1077
}
1065
1078
@@ -1068,6 +1081,7 @@ public async ValueTask BasicPublishAsync<TProperties>(CachedString exchange, Cac
1068
1081
CancellationToken cancellationToken = default )
1069
1082
where TProperties : IReadOnlyBasicProperties , IAmqpHeader
1070
1083
{
1084
+ TaskCompletionSource < bool > ? publisherConfirmationTcs = null ;
1071
1085
if ( _publisherConfirmationsEnabled )
1072
1086
{
1073
1087
await _confirmSemaphore . WaitAsync ( cancellationToken )
@@ -1076,11 +1090,9 @@ await _confirmSemaphore.WaitAsync(cancellationToken)
1076
1090
{
1077
1091
if ( _publisherConfirmationTrackingEnabled )
1078
1092
{
1079
- if ( _pendingDeliveryTags is null )
1080
- {
1081
- throw new InvalidOperationException ( InternalConstants . BugFound ) ;
1082
- }
1083
1093
_pendingDeliveryTags . AddLast ( _nextPublishSeqNo ) ;
1094
+ publisherConfirmationTcs = new ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
1095
+ _confirmsTaskCompletionSources [ _nextPublishSeqNo ] = publisherConfirmationTcs ;
1084
1096
}
1085
1097
1086
1098
_nextPublishSeqNo ++ ;
@@ -1124,7 +1136,7 @@ await ModelSendAsync(in cmd, in basicProperties, body, cancellationToken)
1124
1136
. ConfigureAwait ( false ) ;
1125
1137
}
1126
1138
}
1127
- catch
1139
+ catch ( Exception ex )
1128
1140
{
1129
1141
if ( _publisherConfirmationsEnabled )
1130
1142
{
@@ -1144,7 +1156,21 @@ await _confirmSemaphore.WaitAsync(cancellationToken)
1144
1156
}
1145
1157
}
1146
1158
1147
- throw ;
1159
+ if ( publisherConfirmationTcs is not null )
1160
+ {
1161
+ publisherConfirmationTcs . SetException ( ex ) ;
1162
+ }
1163
+ else
1164
+ {
1165
+ throw ;
1166
+ }
1167
+ }
1168
+
1169
+ if ( publisherConfirmationTcs is not null )
1170
+ {
1171
+ // TODO timeout?
1172
+ await publisherConfirmationTcs . Task
1173
+ . ConfigureAwait ( false ) ;
1148
1174
}
1149
1175
}
1150
1176
@@ -1253,11 +1279,6 @@ await ModelSendAsync(in method, k.CancellationToken)
1253
1279
1254
1280
bool result = await k ;
1255
1281
Debug . Assert ( result ) ;
1256
-
1257
- // Note:
1258
- // Non-null means confirms are enabled
1259
- _confirmSemaphore = new SemaphoreSlim ( 1 , 1 ) ;
1260
-
1261
1282
return ;
1262
1283
}
1263
1284
finally
@@ -1890,10 +1911,6 @@ internal async Task HandleAckNack(ulong deliveryTag, bool multiple, bool isNack,
1890
1911
// Only do this if confirms are enabled *and* the library is tracking confirmations
1891
1912
if ( _publisherConfirmationsEnabled && _publisherConfirmationTrackingEnabled )
1892
1913
{
1893
- if ( _pendingDeliveryTags is null )
1894
- {
1895
- throw new InvalidOperationException ( InternalConstants . BugFound ) ;
1896
- }
1897
1914
// let's take a lock so we can assume that deliveryTags are unique, never duplicated and always sorted
1898
1915
await _confirmSemaphore . WaitAsync ( cancellationToken )
1899
1916
. ConfigureAwait ( false ) ;
@@ -1904,28 +1921,45 @@ await _confirmSemaphore.WaitAsync(cancellationToken)
1904
1921
{
1905
1922
if ( multiple )
1906
1923
{
1907
- while ( _pendingDeliveryTags . First ! . Value < deliveryTag )
1908
- {
1909
- _pendingDeliveryTags . RemoveFirst ( ) ;
1910
- }
1911
-
1912
- if ( _pendingDeliveryTags . First . Value == deliveryTag )
1924
+ do
1913
1925
{
1914
- _pendingDeliveryTags . RemoveFirst ( ) ;
1926
+ if ( _pendingDeliveryTags . First is null )
1927
+ {
1928
+ break ;
1929
+ }
1930
+ else
1931
+ {
1932
+ ulong pendingDeliveryTag = _pendingDeliveryTags . First . Value ;
1933
+ if ( pendingDeliveryTag > deliveryTag )
1934
+ {
1935
+ break ;
1936
+ }
1937
+ else
1938
+ {
1939
+ TaskCompletionSource < bool > tcs = _confirmsTaskCompletionSources [ pendingDeliveryTag ] ;
1940
+ tcs . SetResult ( true ) ;
1941
+ _confirmsTaskCompletionSources . Remove ( pendingDeliveryTag ) ;
1942
+ _pendingDeliveryTags . RemoveFirst ( ) ;
1943
+ }
1944
+ }
1915
1945
}
1946
+ while ( true ) ;
1916
1947
}
1917
1948
else
1918
1949
{
1950
+ TaskCompletionSource < bool > tcs = _confirmsTaskCompletionSources [ deliveryTag ] ;
1951
+ tcs . SetResult ( true ) ;
1952
+ _confirmsTaskCompletionSources . Remove ( deliveryTag ) ;
1919
1953
_pendingDeliveryTags . Remove ( deliveryTag ) ;
1920
1954
}
1921
1955
}
1922
1956
1923
- _onlyAcksReceived = _onlyAcksReceived && ! isNack ;
1957
+ _onlyAcksReceived = _onlyAcksReceived && false == isNack ;
1924
1958
1925
- if ( _pendingDeliveryTags . Count == 0 && _confirmsTaskCompletionSources ! . Count > 0 )
1959
+ if ( _pendingDeliveryTags . Count == 0 && _confirmsTaskCompletionSources . Count > 0 )
1926
1960
{
1927
1961
// Done, mark tasks
1928
- foreach ( TaskCompletionSource < bool > tcs in _confirmsTaskCompletionSources )
1962
+ foreach ( TaskCompletionSource < bool > tcs in _confirmsTaskCompletionSources . Values )
1929
1963
{
1930
1964
tcs . TrySetResult ( _onlyAcksReceived ) ;
1931
1965
}
0 commit comments