15
15
16
16
using System ;
17
17
using System . Buffers . Binary ;
18
- using System . Collections . Concurrent ;
19
18
using System . Diagnostics ;
20
19
using System . IO ;
21
20
using System . Net ;
@@ -46,15 +45,12 @@ internal sealed class BinaryConnection : IConnection
46
45
private ConnectionInitializerContext _connectionInitializerContext ;
47
46
private EndPoint _endPoint ;
48
47
private ConnectionDescription _description ;
49
- private readonly Dropbox _dropbox = new Dropbox ( ) ;
50
48
private bool _failedEventHasBeenRaised ;
51
49
private DateTime _lastUsedAtUtc ;
52
50
private DateTime _openedAtUtc ;
53
51
private readonly object _openLock = new object ( ) ;
54
52
private Task _openTask ;
55
- private readonly SemaphoreSlim _receiveLock ;
56
53
private CompressorType ? _sendCompressorType ;
57
- private readonly SemaphoreSlim _sendLock ;
58
54
private readonly ConnectionSettings _settings ;
59
55
private readonly InterlockedInt32 _state ;
60
56
private Stream _stream ;
@@ -79,8 +75,6 @@ public BinaryConnection(
79
75
Ensure . IsNotNull ( eventSubscriber , nameof ( eventSubscriber ) ) ;
80
76
81
77
_connectionId = new ConnectionId ( serverId , settings . ConnectionIdLocalValueProvider ( ) ) ;
82
- _receiveLock = new SemaphoreSlim ( 1 ) ;
83
- _sendLock = new SemaphoreSlim ( 1 ) ;
84
78
_state = new InterlockedInt32 ( State . Initial ) ;
85
79
86
80
_compressorSource = new CompressorSource ( settings . Compressors ) ;
@@ -173,9 +167,6 @@ private void Dispose(bool disposing)
173
167
_eventLogger . LogAndPublish ( new ConnectionClosingEvent ( _connectionId , EventContext . OperationId ) ) ;
174
168
175
169
var stopwatch = Stopwatch . StartNew ( ) ;
176
- _receiveLock . Dispose ( ) ;
177
- _sendLock . Dispose ( ) ;
178
-
179
170
if ( _stream != null )
180
171
{
181
172
try
@@ -356,50 +347,6 @@ private IByteBuffer ReceiveBuffer(OperationContext operationContext)
356
347
}
357
348
}
358
349
359
- private IByteBuffer ReceiveBuffer ( OperationContext operationContext , int responseTo )
360
- {
361
- using ( var receiveLockRequest = new SemaphoreSlimRequest ( _receiveLock , operationContext . RemainingTimeout , operationContext . CancellationToken ) )
362
- {
363
- var messageTask = _dropbox . GetMessageAsync ( responseTo ) ;
364
- try
365
- {
366
- Task . WaitAny ( messageTask , receiveLockRequest . Task ) ;
367
- if ( messageTask . IsCompleted )
368
- {
369
- return _dropbox . RemoveMessage ( responseTo ) ; // also propagates exception if any
370
- }
371
-
372
- receiveLockRequest . Task . GetAwaiter ( ) . GetResult ( ) ; // propagate exceptions
373
- while ( true )
374
- {
375
- try
376
- {
377
- var buffer = ReceiveBuffer ( operationContext ) ;
378
- _dropbox . AddMessage ( buffer ) ;
379
- }
380
- catch ( Exception ex )
381
- {
382
- _dropbox . AddException ( ex ) ;
383
- }
384
-
385
- if ( messageTask . IsCompleted )
386
- {
387
- return _dropbox . RemoveMessage ( responseTo ) ; // also propagates exception if any
388
- }
389
-
390
- operationContext . ThrowIfTimedOutOrCanceled ( ) ;
391
- }
392
- }
393
- catch
394
- {
395
- var ignored = messageTask . ContinueWith (
396
- t => { _dropbox . RemoveMessage ( responseTo ) . Dispose ( ) ; } ,
397
- TaskContinuationOptions . OnlyOnRanToCompletion ) ;
398
- throw ;
399
- }
400
- }
401
- }
402
-
403
350
private async Task < IByteBuffer > ReceiveBufferAsync ( OperationContext operationContext )
404
351
{
405
352
try
@@ -425,50 +372,6 @@ private async Task<IByteBuffer> ReceiveBufferAsync(OperationContext operationCon
425
372
}
426
373
}
427
374
428
- private async Task < IByteBuffer > ReceiveBufferAsync ( OperationContext operationContext , int responseTo )
429
- {
430
- using ( var receiveLockRequest = new SemaphoreSlimRequest ( _receiveLock , operationContext . RemainingTimeout , operationContext . CancellationToken ) )
431
- {
432
- var messageTask = _dropbox . GetMessageAsync ( responseTo ) ;
433
- try
434
- {
435
- await Task . WhenAny ( messageTask , receiveLockRequest . Task ) . ConfigureAwait ( false ) ;
436
- if ( messageTask . IsCompleted )
437
- {
438
- return _dropbox . RemoveMessage ( responseTo ) ; // also propagates exception if any
439
- }
440
-
441
- await receiveLockRequest . Task . ConfigureAwait ( false ) ; // propagate exceptions
442
- while ( true )
443
- {
444
- try
445
- {
446
- var buffer = await ReceiveBufferAsync ( operationContext ) . ConfigureAwait ( false ) ;
447
- _dropbox . AddMessage ( buffer ) ;
448
- }
449
- catch ( Exception ex )
450
- {
451
- _dropbox . AddException ( ex ) ;
452
- }
453
-
454
- if ( messageTask . IsCompleted )
455
- {
456
- return _dropbox . RemoveMessage ( responseTo ) ; // also propagates exception if any
457
- }
458
-
459
- operationContext . ThrowIfTimedOutOrCanceled ( ) ;
460
- }
461
- }
462
- catch
463
- {
464
- var ignored = messageTask . ContinueWith (
465
- t => { _dropbox . RemoveMessage ( responseTo ) . Dispose ( ) ; } ,
466
- TaskContinuationOptions . OnlyOnRanToCompletion ) ;
467
- throw ;
468
- }
469
- }
470
- }
471
-
472
375
public ResponseMessage ReceiveMessage (
473
376
OperationContext operationContext ,
474
377
int responseTo ,
@@ -482,11 +385,19 @@ public ResponseMessage ReceiveMessage(
482
385
try
483
386
{
484
387
helper . ReceivingMessage ( ) ;
485
- using ( var buffer = ReceiveBuffer ( operationContext , responseTo ) )
388
+ while ( true )
486
389
{
487
- var message = helper . DecodeMessage ( operationContext , buffer , encoderSelector ) ;
488
- helper . ReceivedMessage ( buffer , message ) ;
489
- return message ;
390
+ using ( var buffer = ReceiveBuffer ( operationContext ) )
391
+ {
392
+ if ( responseTo != GetResponseTo ( buffer ) )
393
+ {
394
+ continue ;
395
+ }
396
+
397
+ var message = helper . DecodeMessage ( operationContext , buffer , encoderSelector ) ;
398
+ helper . ReceivedMessage ( buffer , message ) ;
399
+ return message ;
400
+ }
490
401
}
491
402
}
492
403
catch ( Exception ex )
@@ -497,7 +408,9 @@ public ResponseMessage ReceiveMessage(
497
408
}
498
409
}
499
410
500
- public async Task < ResponseMessage > ReceiveMessageAsync ( OperationContext operationContext , int responseTo ,
411
+ public async Task < ResponseMessage > ReceiveMessageAsync (
412
+ OperationContext operationContext ,
413
+ int responseTo ,
501
414
IMessageEncoderSelector encoderSelector ,
502
415
MessageEncoderSettings messageEncoderSettings )
503
416
{
@@ -508,11 +421,19 @@ public async Task<ResponseMessage> ReceiveMessageAsync(OperationContext operatio
508
421
try
509
422
{
510
423
helper . ReceivingMessage ( ) ;
511
- using ( var buffer = await ReceiveBufferAsync ( operationContext , responseTo ) . ConfigureAwait ( false ) )
424
+ while ( true )
512
425
{
513
- var message = helper . DecodeMessage ( operationContext , buffer , encoderSelector ) ;
514
- helper . ReceivedMessage ( buffer , message ) ;
515
- return message ;
426
+ using ( var buffer = await ReceiveBufferAsync ( operationContext ) . ConfigureAwait ( false ) )
427
+ {
428
+ if ( responseTo != GetResponseTo ( buffer ) )
429
+ {
430
+ continue ;
431
+ }
432
+
433
+ var message = helper . DecodeMessage ( operationContext , buffer , encoderSelector ) ;
434
+ helper . ReceivedMessage ( buffer , message ) ;
435
+ return message ;
436
+ }
516
437
}
517
438
}
518
439
catch ( Exception ex )
@@ -523,59 +444,39 @@ public async Task<ResponseMessage> ReceiveMessageAsync(OperationContext operatio
523
444
}
524
445
}
525
446
447
+ private int GetResponseTo ( IByteBuffer message )
448
+ {
449
+ var backingBytes = message . AccessBackingBytes ( 8 ) ;
450
+ return BitConverter . ToInt32 ( backingBytes . Array , backingBytes . Offset ) ;
451
+ }
452
+
526
453
private void SendBuffer ( OperationContext operationContext , IByteBuffer buffer )
527
454
{
528
- _sendLock . Wait ( operationContext . RemainingTimeout , operationContext . CancellationToken ) ;
529
455
try
530
456
{
531
- if ( _state . Value == State . Failed )
532
- {
533
- throw new MongoConnectionClosedException ( _connectionId ) ;
534
- }
535
-
536
- try
537
- {
538
- _stream . WriteBytes ( operationContext , buffer , 0 , buffer . Length ) ;
539
- _lastUsedAtUtc = DateTime . UtcNow ;
540
- }
541
- catch ( Exception ex )
542
- {
543
- var wrappedException = WrapExceptionIfRequired ( ex , "sending a message to the server" ) ;
544
- ConnectionFailed ( wrappedException ?? ex ) ;
545
- if ( wrappedException == null ) { throw ; } else { throw wrappedException ; }
546
- }
457
+ _stream . WriteBytes ( operationContext , buffer , 0 , buffer . Length ) ;
458
+ _lastUsedAtUtc = DateTime . UtcNow ;
547
459
}
548
- finally
460
+ catch ( Exception ex )
549
461
{
550
- _sendLock . Release ( ) ;
462
+ var wrappedException = WrapExceptionIfRequired ( ex , "sending a message to the server" ) ;
463
+ ConnectionFailed ( wrappedException ?? ex ) ;
464
+ if ( wrappedException == null ) { throw ; } else { throw wrappedException ; }
551
465
}
552
466
}
553
467
554
468
private async Task SendBufferAsync ( OperationContext operationContext , IByteBuffer buffer )
555
469
{
556
- await _sendLock . WaitAsync ( operationContext . RemainingTimeout , operationContext . CancellationToken ) . ConfigureAwait ( false ) ;
557
470
try
558
471
{
559
- if ( _state . Value == State . Failed )
560
- {
561
- throw new MongoConnectionClosedException ( _connectionId ) ;
562
- }
563
-
564
- try
565
- {
566
- await _stream . WriteBytesAsync ( operationContext , buffer , 0 , buffer . Length ) . ConfigureAwait ( false ) ;
567
- _lastUsedAtUtc = DateTime . UtcNow ;
568
- }
569
- catch ( Exception ex )
570
- {
571
- var wrappedException = WrapExceptionIfRequired ( ex , "sending a message to the server" ) ;
572
- ConnectionFailed ( wrappedException ?? ex ) ;
573
- if ( wrappedException == null ) { throw ; } else { throw wrappedException ; }
574
- }
472
+ await _stream . WriteBytesAsync ( operationContext , buffer , 0 , buffer . Length ) . ConfigureAwait ( false ) ;
473
+ _lastUsedAtUtc = DateTime . UtcNow ;
575
474
}
576
- finally
475
+ catch ( Exception ex )
577
476
{
578
- _sendLock . Release ( ) ;
477
+ var wrappedException = WrapExceptionIfRequired ( ex , "sending a message to the server" ) ;
478
+ ConnectionFailed ( wrappedException ?? ex ) ;
479
+ if ( wrappedException == null ) { throw ; } else { throw wrappedException ; }
579
480
}
580
481
}
581
482
@@ -770,47 +671,6 @@ private void ThrowOperationCanceledExceptionIfRequired(Exception exception)
770
671
}
771
672
772
673
// nested classes
773
- private class Dropbox
774
- {
775
- private readonly ConcurrentDictionary < int , TaskCompletionSource < IByteBuffer > > _messages = new ConcurrentDictionary < int , TaskCompletionSource < IByteBuffer > > ( ) ;
776
-
777
- // public methods
778
- public void AddException ( Exception exception )
779
- {
780
- foreach ( var taskCompletionSource in _messages . Values )
781
- {
782
- taskCompletionSource . TrySetException ( exception ) ; // has no effect on already completed tasks
783
- }
784
- }
785
-
786
- public void AddMessage ( IByteBuffer message )
787
- {
788
- var responseTo = GetResponseTo ( message ) ;
789
- var tcs = _messages . GetOrAdd ( responseTo , x => new TaskCompletionSource < IByteBuffer > ( ) ) ;
790
- tcs . TrySetResult ( message ) ;
791
- }
792
-
793
- public Task < IByteBuffer > GetMessageAsync ( int responseTo )
794
- {
795
- var tcs = _messages . GetOrAdd ( responseTo , _ => new TaskCompletionSource < IByteBuffer > ( ) ) ;
796
- return tcs . Task ;
797
- }
798
-
799
- public IByteBuffer RemoveMessage ( int responseTo )
800
- {
801
- TaskCompletionSource < IByteBuffer > tcs ;
802
- _messages . TryRemove ( responseTo , out tcs ) ;
803
- return tcs . Task . GetAwaiter ( ) . GetResult ( ) ; // RemoveMessage is only called when Task is complete
804
- }
805
-
806
- // private methods
807
- private int GetResponseTo ( IByteBuffer message )
808
- {
809
- var backingBytes = message . AccessBackingBytes ( 8 ) ;
810
- return BinaryPrimitives . ReadInt32LittleEndian ( new ReadOnlySpan < byte > ( backingBytes . Array , backingBytes . Offset , 4 ) ) ;
811
- }
812
- }
813
-
814
674
private class OpenConnectionHelper
815
675
{
816
676
private readonly BinaryConnection _connection ;
0 commit comments