2929// Copyright (c) 2007-2024 Broadcom. All Rights Reserved.
3030//---------------------------------------------------------------------------
3131
32+ using System ;
3233using System . Buffers . Binary ;
3334using System . Collections . Concurrent ;
3435using System . Collections . Generic ;
@@ -52,6 +53,70 @@ internal partial class Channel : IChannel, IRecoverable
5253 private readonly SemaphoreSlim _confirmSemaphore = new ( 1 , 1 ) ;
5354 private readonly ConcurrentDictionary < ulong , TaskCompletionSource < bool > > _confirmsTaskCompletionSources = new ( ) ;
5455
56+ private class PublisherConfirmationInfo
57+ {
58+ private ulong _publishSequenceNumber ;
59+ private TaskCompletionSource < bool > ? _publisherConfirmationTcs ;
60+
61+ internal PublisherConfirmationInfo ( )
62+ {
63+ _publishSequenceNumber = 0 ;
64+ _publisherConfirmationTcs = null ;
65+ }
66+
67+ internal PublisherConfirmationInfo ( ulong publishSequenceNumber , TaskCompletionSource < bool > ? publisherConfirmationTcs )
68+ {
69+ _publishSequenceNumber = publishSequenceNumber ;
70+ _publisherConfirmationTcs = publisherConfirmationTcs ;
71+ }
72+
73+ internal ulong PublishSequenceNumber => _publishSequenceNumber ;
74+
75+ internal TaskCompletionSource < bool > ? PublisherConfirmationTcs => _publisherConfirmationTcs ;
76+
77+ internal async Task MaybeWaitForConfirmationAsync ( CancellationToken cancellationToken )
78+ {
79+ if ( _publisherConfirmationTcs is not null )
80+ {
81+ await _publisherConfirmationTcs . Task . WaitAsync ( cancellationToken )
82+ . ConfigureAwait ( false ) ;
83+ }
84+ }
85+
86+ internal bool MaybeHandleException ( Exception ex )
87+ {
88+ bool exceptionWasHandled = false ;
89+
90+ if ( _publisherConfirmationTcs is not null )
91+ {
92+ _publisherConfirmationTcs . SetException ( ex ) ;
93+ exceptionWasHandled = true ;
94+ }
95+
96+ return exceptionWasHandled ;
97+ }
98+ }
99+
100+ public async ValueTask < ulong > GetNextPublishSequenceNumberAsync ( CancellationToken cancellationToken = default )
101+ {
102+ if ( _publisherConfirmationsEnabled )
103+ {
104+ await _confirmSemaphore . WaitAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
105+ try
106+ {
107+ return _nextPublishSeqNo ;
108+ }
109+ finally
110+ {
111+ _confirmSemaphore . Release ( ) ;
112+ }
113+ }
114+ else
115+ {
116+ return _nextPublishSeqNo ;
117+ }
118+ }
119+
55120 private void ConfigurePublisherConfirmations ( bool publisherConfirmationsEnabled , bool publisherConfirmationTrackingEnabled )
56121 {
57122 _publisherConfirmationsEnabled = publisherConfirmationsEnabled ;
@@ -98,10 +163,17 @@ await ModelSendAsync(in method, k.CancellationToken)
98163 }
99164 }
100165
166+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
167+ private bool ShouldHandleAckOrNack ( ulong deliveryTag )
168+ {
169+ return _publisherConfirmationsEnabled && _publisherConfirmationTrackingEnabled &&
170+ deliveryTag > 0 && ! _confirmsTaskCompletionSources . IsEmpty ;
171+ }
172+
101173 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
102174 private void HandleAck ( ulong deliveryTag , bool multiple )
103175 {
104- if ( _publisherConfirmationsEnabled && _publisherConfirmationTrackingEnabled && deliveryTag > 0 && ! _confirmsTaskCompletionSources . IsEmpty )
176+ if ( ShouldHandleAckOrNack ( deliveryTag ) )
105177 {
106178 if ( multiple )
107179 {
@@ -127,7 +199,7 @@ private void HandleAck(ulong deliveryTag, bool multiple)
127199 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
128200 private void HandleNack ( ulong deliveryTag , bool multiple , bool isReturn )
129201 {
130- if ( _publisherConfirmationsEnabled && _publisherConfirmationTrackingEnabled && deliveryTag > 0 && ! _confirmsTaskCompletionSources . IsEmpty )
202+ if ( ShouldHandleAckOrNack ( deliveryTag ) )
131203 {
132204 if ( multiple )
133205 {
@@ -192,5 +264,67 @@ await _confirmSemaphore.WaitAsync(reason.CancellationToken)
192264 }
193265 }
194266 }
267+
268+ private async Task < PublisherConfirmationInfo ? > MaybeStartPublisherConfirmationTracking ( CancellationToken cancellationToken )
269+ {
270+ if ( _publisherConfirmationsEnabled )
271+ {
272+ await _confirmSemaphore . WaitAsync ( cancellationToken )
273+ . ConfigureAwait ( false ) ;
274+
275+ ulong publishSequenceNumber = _nextPublishSeqNo ;
276+
277+ TaskCompletionSource < bool > ? publisherConfirmationTcs = null ;
278+ if ( _publisherConfirmationTrackingEnabled )
279+ {
280+ publisherConfirmationTcs = new TaskCompletionSource < bool > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
281+ _confirmsTaskCompletionSources [ publishSequenceNumber ] = publisherConfirmationTcs ;
282+ }
283+
284+ _nextPublishSeqNo ++ ;
285+
286+ return new PublisherConfirmationInfo ( publishSequenceNumber , publisherConfirmationTcs ) ;
287+ }
288+ else
289+ {
290+ return null ;
291+ }
292+ }
293+
294+ private bool MaybeHandleExceptionWithEnabledPublisherConfirmations ( PublisherConfirmationInfo ? publisherConfirmationInfo ,
295+ Exception ex )
296+ {
297+ bool exceptionWasHandled = false ;
298+
299+ if ( _publisherConfirmationsEnabled &&
300+ publisherConfirmationInfo is not null )
301+ {
302+ _nextPublishSeqNo -- ;
303+
304+ if ( _publisherConfirmationTrackingEnabled )
305+ {
306+ _confirmsTaskCompletionSources . TryRemove ( publisherConfirmationInfo . PublishSequenceNumber , out _ ) ;
307+ }
308+
309+ exceptionWasHandled = publisherConfirmationInfo . MaybeHandleException ( ex ) ;
310+ }
311+
312+ return exceptionWasHandled ;
313+ }
314+
315+ private async Task MaybeEndPublisherConfirmationTracking ( PublisherConfirmationInfo ? publisherConfirmationInfo ,
316+ CancellationToken cancellationToken )
317+ {
318+ if ( _publisherConfirmationsEnabled )
319+ {
320+ _confirmSemaphore . Release ( ) ;
321+
322+ if ( publisherConfirmationInfo is not null )
323+ {
324+ await publisherConfirmationInfo . MaybeWaitForConfirmationAsync ( cancellationToken )
325+ . ConfigureAwait ( false ) ;
326+ }
327+ }
328+ }
195329 }
196330}
0 commit comments