@@ -49,9 +49,11 @@ namespace RabbitMQ.Client.Impl
49
49
internal abstract class ChannelBase : IChannel , IRecoverable
50
50
{
51
51
///<summary>Only used to kick-start a connection open
52
- ///sequence. See <see cref="Connection.Open "/> </summary>
53
- internal BlockingCell < ConnectionStartDetails > m_connectionStartCell ;
52
+ ///sequence. See <see cref="Connection.OpenAsync "/> </summary>
53
+ internal TaskCompletionSource < ConnectionStartDetails > m_connectionStartCell ;
54
54
55
+ // AMQP only allows one RPC operation to be active at a time.
56
+ private readonly SemaphoreSlim _rpcSemaphore = new SemaphoreSlim ( 1 , 1 ) ;
55
57
private readonly RpcContinuationQueue _continuationQueue = new RpcContinuationQueue ( ) ;
56
58
private readonly ManualResetEventSlim _flowControlBlock = new ManualResetEventSlim ( true ) ;
57
59
@@ -258,13 +260,19 @@ internal void ConnectionOpen(string virtualHost)
258
260
k . GetReply ( HandshakeContinuationTimeout ) ;
259
261
}
260
262
}
263
+
264
+ internal ValueTask ConnectionOpenAsync ( string virtualHost )
265
+ {
266
+ return _Private_ConnectionOpenAsync ( virtualHost ) ;
267
+ }
261
268
262
- internal ConnectionSecureOrTune ConnectionSecureOk ( byte [ ] response )
269
+ internal async ValueTask < ConnectionSecureOrTune > ConnectionSecureOkAsync ( byte [ ] response )
263
270
{
264
- var k = new ConnectionStartRpcContinuation ( ) ;
265
- lock ( _rpcLock )
271
+ var k = new ConnectionSecureOrTuneContinuation ( ) ;
272
+ await _rpcSemaphore . WaitAsync ( ) . ConfigureAwait ( false ) ;
273
+ Enqueue ( k ) ;
274
+ try
266
275
{
267
- Enqueue ( k ) ;
268
276
try
269
277
{
270
278
_Private_ConnectionSecureOk ( response ) ;
@@ -275,31 +283,40 @@ internal ConnectionSecureOrTune ConnectionSecureOk(byte[] response)
275
283
// which is a much more suitable exception before connection
276
284
// negotiation finishes
277
285
}
278
- k . GetReply ( HandshakeContinuationTimeout ) ;
286
+
287
+ return await k ;
288
+ }
289
+ finally
290
+ {
291
+ _rpcSemaphore . Release ( ) ;
279
292
}
280
- return k . m_result ;
281
293
}
282
294
283
- internal ConnectionSecureOrTune ConnectionStartOk ( IDictionary < string , object > clientProperties , string mechanism , byte [ ] response , string locale )
295
+ internal async ValueTask < ConnectionSecureOrTune > ConnectionStartOkAsync ( IDictionary < string , object > clientProperties , string mechanism , byte [ ] response ,
296
+ string locale )
284
297
{
285
- var k = new ConnectionStartRpcContinuation ( ) ;
286
- lock ( _rpcLock )
298
+ var k = new ConnectionSecureOrTuneContinuation ( ) ;
299
+ await _rpcSemaphore . WaitAsync ( ) . ConfigureAwait ( false ) ;
300
+ Enqueue ( k ) ;
301
+ try
287
302
{
288
- Enqueue ( k ) ;
289
303
try
290
304
{
291
- _Private_ConnectionStartOk ( clientProperties , mechanism ,
292
- response , locale ) ;
305
+ _Private_ConnectionStartOk ( clientProperties , mechanism , response , locale ) ;
293
306
}
294
307
catch ( AlreadyClosedException )
295
308
{
296
309
// let continuation throw OperationInterruptedException,
297
310
// which is a much more suitable exception before connection
298
311
// negotiation finishes
299
312
}
300
- k . GetReply ( HandshakeContinuationTimeout ) ;
313
+
314
+ return await k ;
315
+ }
316
+ finally
317
+ {
318
+ _rpcSemaphore . Release ( ) ;
301
319
}
302
- return k . m_result ;
303
320
}
304
321
305
322
protected abstract bool DispatchAsynchronous ( in IncomingCommand cmd ) ;
@@ -324,7 +341,7 @@ internal void FinishClose()
324
341
Session . Close ( reason ) ;
325
342
}
326
343
327
- m_connectionStartCell ? . ContinueWithValue ( null ) ;
344
+ m_connectionStartCell ? . TrySetResult ( null ) ;
328
345
}
329
346
330
347
private void HandleCommand ( in IncomingCommand cmd )
@@ -384,6 +401,12 @@ protected void ChannelSend<T>(in T method) where T : struct, IOutgoingAmqpMethod
384
401
{
385
402
Session . Transmit ( in method ) ;
386
403
}
404
+
405
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
406
+ protected ValueTask ModelSendAsync < T > ( in T method ) where T : struct , IOutgoingAmqpMethod
407
+ {
408
+ return Session . TransmitAsync ( in method ) ;
409
+ }
387
410
388
411
[ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
389
412
protected void ChannelSend < TMethod , THeader > ( in TMethod method , in THeader header , ReadOnlyMemory < byte > body )
@@ -396,6 +419,19 @@ protected void ChannelSend<TMethod, THeader>(in TMethod method, in THeader heade
396
419
}
397
420
Session . Transmit ( in method , in header , body ) ;
398
421
}
422
+
423
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
424
+ protected ValueTask ModelSendAsync < TMethod , THeader > ( in TMethod method , in THeader header , ReadOnlyMemory < byte > body )
425
+ where TMethod : struct , IOutgoingAmqpMethod
426
+ where THeader : IAmqpHeader
427
+ {
428
+ if ( ! _flowControlBlock . IsSet )
429
+ {
430
+ _flowControlBlock . Wait ( ) ;
431
+ }
432
+
433
+ return Session . TransmitAsync ( in method , in header , body ) ;
434
+ }
399
435
400
436
internal void OnCallbackException ( CallbackExceptionEventArgs args )
401
437
{
@@ -730,13 +766,7 @@ protected void HandleConnectionClose(in IncomingCommand cmd)
730
766
731
767
protected void HandleConnectionSecure ( in IncomingCommand cmd )
732
768
{
733
- var challenge = new ConnectionSecure ( cmd . MethodBytes . Span ) . _challenge ;
734
- cmd . ReturnMethodBuffer ( ) ;
735
- var k = ( ConnectionStartRpcContinuation ) _continuationQueue . Next ( ) ;
736
- k . m_result = new ConnectionSecureOrTune
737
- {
738
- m_challenge = challenge
739
- } ;
769
+ var k = ( ConnectionSecureOrTuneContinuation ) _continuationQueue . Next ( ) ;
740
770
k . HandleCommand ( IncomingCommand . Empty ) ; // release the continuation.
741
771
}
742
772
@@ -758,25 +788,14 @@ protected void HandleConnectionStart(in IncomingCommand cmd)
758
788
m_mechanisms = method . _mechanisms ,
759
789
m_locales = method . _locales
760
790
} ;
761
- m_connectionStartCell . ContinueWithValue ( details ) ;
791
+ m_connectionStartCell ? . SetResult ( details ) ;
762
792
m_connectionStartCell = null ;
763
793
}
764
794
765
795
protected void HandleConnectionTune ( in IncomingCommand cmd )
766
796
{
767
- var connectionTune = new ConnectionTune ( cmd . MethodBytes . Span ) ;
768
- cmd . ReturnMethodBuffer ( ) ;
769
- var k = ( ConnectionStartRpcContinuation ) _continuationQueue . Next ( ) ;
770
- k . m_result = new ConnectionSecureOrTune
771
- {
772
- m_tuneDetails =
773
- {
774
- m_channelMax = connectionTune . _channelMax ,
775
- m_frameMax = connectionTune . _frameMax ,
776
- m_heartbeatInSeconds = connectionTune . _heartbeat
777
- }
778
- } ;
779
- k . HandleCommand ( IncomingCommand . Empty ) ; // release the continuation.
797
+ var k = ( ConnectionSecureOrTuneContinuation ) _continuationQueue . Next ( ) ;
798
+ k . HandleCommand ( cmd ) ; // release the continuation.
780
799
}
781
800
782
801
protected void HandleConnectionUnblocked ( )
@@ -815,6 +834,8 @@ protected void HandleQueueDeclareOk(in IncomingCommand cmd)
815
834
816
835
public abstract void _Private_ConnectionOpen ( string virtualHost ) ;
817
836
837
+ public abstract ValueTask _Private_ConnectionOpenAsync ( string virtualHost ) ;
838
+
818
839
public abstract void _Private_ConnectionSecureOk ( byte [ ] response ) ;
819
840
820
841
public abstract void _Private_ConnectionStartOk ( IDictionary < string , object > clientProperties , string mechanism , byte [ ] response , string locale ) ;
@@ -929,6 +950,36 @@ public void BasicPublish<TProperties>(CachedString exchange, CachedString routin
929
950
var cmd = new BasicPublishMemory ( exchange . Bytes , routingKey . Bytes , mandatory , default ) ;
930
951
ChannelSend ( in cmd , in basicProperties , body ) ;
931
952
}
953
+
954
+ public ValueTask BasicPublishAsync < TProperties > ( string exchange , string routingKey , in TProperties basicProperties , ReadOnlyMemory < byte > body , bool mandatory )
955
+ where TProperties : IReadOnlyBasicProperties , IAmqpHeader
956
+ {
957
+ if ( NextPublishSeqNo > 0 )
958
+ {
959
+ lock ( _confirmLock )
960
+ {
961
+ _pendingDeliveryTags . AddLast ( NextPublishSeqNo ++ ) ;
962
+ }
963
+ }
964
+
965
+ var cmd = new BasicPublish ( exchange , routingKey , mandatory , default ) ;
966
+ return ModelSendAsync ( in cmd , in basicProperties , body ) ;
967
+ }
968
+
969
+ public ValueTask BasicPublishAsync < TProperties > ( CachedString exchange , CachedString routingKey , in TProperties basicProperties , ReadOnlyMemory < byte > body , bool mandatory )
970
+ where TProperties : IReadOnlyBasicProperties , IAmqpHeader
971
+ {
972
+ if ( NextPublishSeqNo > 0 )
973
+ {
974
+ lock ( _confirmLock )
975
+ {
976
+ _pendingDeliveryTags . AddLast ( NextPublishSeqNo ++ ) ;
977
+ }
978
+ }
979
+
980
+ var cmd = new BasicPublishMemory ( exchange . Bytes , routingKey . Bytes , mandatory , default ) ;
981
+ return ModelSendAsync ( in cmd , in basicProperties , body ) ;
982
+ }
932
983
933
984
public void UpdateSecret ( string newSecret , string reason )
934
985
{
0 commit comments