@@ -40,8 +40,6 @@ namespace MongoDB.Driver.Core.Connections
40
40
internal class BinaryConnection : IConnection
41
41
{
42
42
// fields
43
- private readonly CancellationToken _backgroundTaskCancellationToken ;
44
- private readonly CancellationTokenSource _backgroundTaskCancellationTokenSource ;
45
43
private readonly CommandEventHelper _commandEventHelper ;
46
44
private readonly ICompressorSource _compressorSource ;
47
45
private ConnectionId _connectionId ;
@@ -85,9 +83,6 @@ public BinaryConnection(ServerId serverId, EndPoint endPoint, ConnectionSettings
85
83
_connectionInitializer = Ensure . IsNotNull ( connectionInitializer , nameof ( connectionInitializer ) ) ;
86
84
Ensure . IsNotNull ( eventSubscriber , nameof ( eventSubscriber ) ) ;
87
85
88
- _backgroundTaskCancellationTokenSource = new CancellationTokenSource ( ) ;
89
- _backgroundTaskCancellationToken = _backgroundTaskCancellationTokenSource . Token ;
90
-
91
86
_connectionId = new ConnectionId ( serverId ) ;
92
87
_receiveLock = new SemaphoreSlim ( 1 ) ;
93
88
_sendLock = new SemaphoreSlim ( 1 ) ;
@@ -199,8 +194,6 @@ private void Dispose(bool disposing)
199
194
}
200
195
201
196
var stopwatch = Stopwatch . StartNew ( ) ;
202
- _backgroundTaskCancellationTokenSource . Cancel ( ) ;
203
- _backgroundTaskCancellationTokenSource . Dispose ( ) ;
204
197
_receiveLock . Dispose ( ) ;
205
198
_sendLock . Dispose ( ) ;
206
199
@@ -237,7 +230,7 @@ private void EnsureMessageSizeIsValid(int messageSize)
237
230
238
231
public void Open ( CancellationToken cancellationToken )
239
232
{
240
- ThrowIfDisposed ( ) ;
233
+ ThrowIfCancelledOrDisposed ( cancellationToken ) ;
241
234
242
235
TaskCompletionSource < bool > taskCompletionSource = null ;
243
236
var connecting = false ;
@@ -274,7 +267,7 @@ public void Open(CancellationToken cancellationToken)
274
267
275
268
public Task OpenAsync ( CancellationToken cancellationToken )
276
269
{
277
- ThrowIfDisposed ( ) ;
270
+ ThrowIfCancelledOrDisposed ( cancellationToken ) ;
278
271
279
272
lock ( _openLock )
280
273
{
@@ -329,19 +322,19 @@ private async Task OpenHelperAsync(CancellationToken cancellationToken)
329
322
}
330
323
}
331
324
332
- private IByteBuffer ReceiveBuffer ( )
325
+ private IByteBuffer ReceiveBuffer ( CancellationToken cancellationToken )
333
326
{
334
327
try
335
328
{
336
329
var messageSizeBytes = new byte [ 4 ] ;
337
- _stream . ReadBytes ( messageSizeBytes , 0 , 4 , _backgroundTaskCancellationToken ) ;
330
+ _stream . ReadBytes ( messageSizeBytes , 0 , 4 , cancellationToken ) ;
338
331
var messageSize = BitConverter . ToInt32 ( messageSizeBytes , 0 ) ;
339
332
EnsureMessageSizeIsValid ( messageSize ) ;
340
333
var inputBufferChunkSource = new InputBufferChunkSource ( BsonChunkPool . Default ) ;
341
334
var buffer = ByteBufferFactory . Create ( inputBufferChunkSource , messageSize ) ;
342
335
buffer . Length = messageSize ;
343
336
buffer . SetBytes ( 0 , messageSizeBytes , 0 , 4 ) ;
344
- _stream . ReadBytes ( buffer , 4 , messageSize - 4 , _backgroundTaskCancellationToken ) ;
337
+ _stream . ReadBytes ( buffer , 4 , messageSize - 4 , cancellationToken ) ;
345
338
_lastUsedAtUtc = DateTime . UtcNow ;
346
339
buffer . MakeReadOnly ( ) ;
347
340
return buffer ;
@@ -370,7 +363,7 @@ private IByteBuffer ReceiveBuffer(int responseTo, CancellationToken cancellation
370
363
receiveLockRequest . Task . GetAwaiter ( ) . GetResult ( ) ; // propagate exceptions
371
364
while ( true )
372
365
{
373
- var buffer = ReceiveBuffer ( ) ;
366
+ var buffer = ReceiveBuffer ( cancellationToken ) ;
374
367
_dropbox . AddMessage ( buffer ) ;
375
368
376
369
if ( messageTask . IsCompleted )
@@ -391,20 +384,20 @@ private IByteBuffer ReceiveBuffer(int responseTo, CancellationToken cancellation
391
384
}
392
385
}
393
386
394
- private async Task < IByteBuffer > ReceiveBufferAsync ( )
387
+ private async Task < IByteBuffer > ReceiveBufferAsync ( CancellationToken cancellationToken )
395
388
{
396
389
try
397
390
{
398
391
var messageSizeBytes = new byte [ 4 ] ;
399
392
var readTimeout = _stream . CanTimeout ? TimeSpan . FromMilliseconds ( _stream . ReadTimeout ) : Timeout . InfiniteTimeSpan ;
400
- await _stream . ReadBytesAsync ( messageSizeBytes , 0 , 4 , readTimeout , _backgroundTaskCancellationToken ) . ConfigureAwait ( false ) ;
393
+ await _stream . ReadBytesAsync ( messageSizeBytes , 0 , 4 , readTimeout , cancellationToken ) . ConfigureAwait ( false ) ;
401
394
var messageSize = BitConverter . ToInt32 ( messageSizeBytes , 0 ) ;
402
395
EnsureMessageSizeIsValid ( messageSize ) ;
403
396
var inputBufferChunkSource = new InputBufferChunkSource ( BsonChunkPool . Default ) ;
404
397
var buffer = ByteBufferFactory . Create ( inputBufferChunkSource , messageSize ) ;
405
398
buffer . Length = messageSize ;
406
399
buffer . SetBytes ( 0 , messageSizeBytes , 0 , 4 ) ;
407
- await _stream . ReadBytesAsync ( buffer , 4 , messageSize - 4 , readTimeout , _backgroundTaskCancellationToken ) . ConfigureAwait ( false ) ;
400
+ await _stream . ReadBytesAsync ( buffer , 4 , messageSize - 4 , readTimeout , cancellationToken ) . ConfigureAwait ( false ) ;
408
401
_lastUsedAtUtc = DateTime . UtcNow ;
409
402
buffer . MakeReadOnly ( ) ;
410
403
return buffer ;
@@ -433,7 +426,7 @@ private async Task<IByteBuffer> ReceiveBufferAsync(int responseTo, CancellationT
433
426
receiveLockRequest . Task . GetAwaiter ( ) . GetResult ( ) ; // propagate exceptions
434
427
while ( true )
435
428
{
436
- var buffer = await ReceiveBufferAsync ( ) . ConfigureAwait ( false ) ;
429
+ var buffer = await ReceiveBufferAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
437
430
_dropbox . AddMessage ( buffer ) ;
438
431
439
432
if ( messageTask . IsCompleted )
@@ -461,7 +454,7 @@ public ResponseMessage ReceiveMessage(
461
454
CancellationToken cancellationToken )
462
455
{
463
456
Ensure . IsNotNull ( encoderSelector , nameof ( encoderSelector ) ) ;
464
- ThrowIfDisposedOrNotOpen ( ) ;
457
+ ThrowIfCancelledOrDisposedOrNotOpen ( cancellationToken ) ;
465
458
466
459
var helper = new ReceiveMessageHelper ( this , responseTo , messageEncoderSettings , _compressorSource ) ;
467
460
try
@@ -477,7 +470,7 @@ public ResponseMessage ReceiveMessage(
477
470
catch ( Exception ex )
478
471
{
479
472
helper . FailedReceivingMessage ( ex ) ;
480
- throw ;
473
+ throw WrapInOperationCanceledExceptionIfRequired ( ex , cancellationToken ) ;
481
474
}
482
475
}
483
476
@@ -488,7 +481,7 @@ public async Task<ResponseMessage> ReceiveMessageAsync(
488
481
CancellationToken cancellationToken )
489
482
{
490
483
Ensure . IsNotNull ( encoderSelector , nameof ( encoderSelector ) ) ;
491
- ThrowIfDisposedOrNotOpen ( ) ;
484
+ ThrowIfCancelledOrDisposedOrNotOpen ( cancellationToken ) ;
492
485
493
486
var helper = new ReceiveMessageHelper ( this , responseTo , messageEncoderSettings , _compressorSource ) ;
494
487
try
@@ -504,7 +497,7 @@ public async Task<ResponseMessage> ReceiveMessageAsync(
504
497
catch ( Exception ex )
505
498
{
506
499
helper . FailedReceivingMessage ( ex ) ;
507
- throw ;
500
+ throw WrapInOperationCanceledExceptionIfRequired ( ex , cancellationToken ) ;
508
501
}
509
502
}
510
503
@@ -520,8 +513,7 @@ private void SendBuffer(IByteBuffer buffer, CancellationToken cancellationToken)
520
513
521
514
try
522
515
{
523
- // don't use the caller's cancellationToken because once we start writing a message we have to write the whole thing
524
- _stream . WriteBytes ( buffer , 0 , buffer . Length , _backgroundTaskCancellationToken ) ;
516
+ _stream . WriteBytes ( buffer , 0 , buffer . Length , cancellationToken ) ;
525
517
_lastUsedAtUtc = DateTime . UtcNow ;
526
518
}
527
519
catch ( Exception ex )
@@ -549,9 +541,8 @@ private async Task SendBufferAsync(IByteBuffer buffer, CancellationToken cancell
549
541
550
542
try
551
543
{
552
- // don't use the caller's cancellationToken because once we start writing a message we have to write the whole thing
553
544
var writeTimeout = _stream . CanTimeout ? TimeSpan . FromMilliseconds ( _stream . WriteTimeout ) : Timeout . InfiniteTimeSpan ;
554
- await _stream . WriteBytesAsync ( buffer , 0 , buffer . Length , writeTimeout , _backgroundTaskCancellationToken ) . ConfigureAwait ( false ) ;
545
+ await _stream . WriteBytesAsync ( buffer , 0 , buffer . Length , writeTimeout , cancellationToken ) . ConfigureAwait ( false ) ;
555
546
_lastUsedAtUtc = DateTime . UtcNow ;
556
547
}
557
548
catch ( Exception ex )
@@ -570,7 +561,7 @@ private async Task SendBufferAsync(IByteBuffer buffer, CancellationToken cancell
570
561
public void SendMessages ( IEnumerable < RequestMessage > messages , MessageEncoderSettings messageEncoderSettings , CancellationToken cancellationToken )
571
562
{
572
563
Ensure . IsNotNull ( messages , nameof ( messages ) ) ;
573
- ThrowIfDisposedOrNotOpen ( ) ;
564
+ ThrowIfCancelledOrDisposedOrNotOpen ( cancellationToken ) ;
574
565
575
566
var helper = new SendMessagesHelper ( this , messages , messageEncoderSettings ) ;
576
567
try
@@ -599,14 +590,14 @@ public void SendMessages(IEnumerable<RequestMessage> messages, MessageEncoderSet
599
590
catch ( Exception ex )
600
591
{
601
592
helper . FailedSendingMessages ( ex ) ;
602
- throw ;
593
+ throw WrapInOperationCanceledExceptionIfRequired ( ex , cancellationToken ) ;
603
594
}
604
595
}
605
596
606
597
public async Task SendMessagesAsync ( IEnumerable < RequestMessage > messages , MessageEncoderSettings messageEncoderSettings , CancellationToken cancellationToken )
607
598
{
608
599
Ensure . IsNotNull ( messages , nameof ( messages ) ) ;
609
- ThrowIfDisposedOrNotOpen ( ) ;
600
+ ThrowIfCancelledOrDisposedOrNotOpen ( cancellationToken ) ;
610
601
611
602
var helper = new SendMessagesHelper ( this , messages , messageEncoderSettings ) ;
612
603
try
@@ -635,10 +626,16 @@ public async Task SendMessagesAsync(IEnumerable<RequestMessage> messages, Messag
635
626
catch ( Exception ex )
636
627
{
637
628
helper . FailedSendingMessages ( ex ) ;
638
- throw ;
629
+ throw WrapInOperationCanceledExceptionIfRequired ( ex , cancellationToken ) ;
639
630
}
640
631
}
641
632
633
+ public void SetReadTimeout ( TimeSpan timeout )
634
+ {
635
+ ThrowIfDisposed ( ) ;
636
+ _stream . ReadTimeout = ( int ) timeout . TotalMilliseconds ;
637
+ }
638
+
642
639
// private methods
643
640
private bool AnyMessageNeedsToBeCompressed ( IEnumerable < RequestMessage > messages )
644
641
{
@@ -698,17 +695,15 @@ private void CompressMessage(
698
695
compressedMessageEncoder . WriteMessage ( compressedMessage ) ;
699
696
}
700
697
701
- private void ThrowIfDisposed ( )
698
+ private void ThrowIfCancelledOrDisposed ( CancellationToken cancellationToken = default )
702
699
{
703
- if ( _state . Value == State . Disposed )
704
- {
705
- throw new ObjectDisposedException ( GetType ( ) . Name ) ;
706
- }
700
+ cancellationToken . ThrowIfCancellationRequested ( ) ;
701
+ ThrowIfDisposed ( ) ;
707
702
}
708
703
709
- private void ThrowIfDisposedOrNotOpen ( )
704
+ private void ThrowIfCancelledOrDisposedOrNotOpen ( CancellationToken cancellationToken )
710
705
{
711
- ThrowIfDisposed ( ) ;
706
+ ThrowIfCancelledOrDisposed ( cancellationToken ) ;
712
707
if ( _state . Value == State . Failed )
713
708
{
714
709
throw new MongoConnectionClosedException ( _connectionId ) ;
@@ -719,6 +714,14 @@ private void ThrowIfDisposedOrNotOpen()
719
714
}
720
715
}
721
716
717
+ private void ThrowIfDisposed ( )
718
+ {
719
+ if ( _state . Value == State . Disposed )
720
+ {
721
+ throw new ObjectDisposedException ( GetType ( ) . Name ) ;
722
+ }
723
+ }
724
+
722
725
private Exception WrapException ( Exception ex , string action )
723
726
{
724
727
if (
@@ -727,7 +730,9 @@ ex is ThreadAbortException ||
727
730
ex is StackOverflowException ||
728
731
#endif
729
732
ex is MongoAuthenticationException ||
730
- ex is OutOfMemoryException )
733
+ ex is OutOfMemoryException ||
734
+ ex is OperationCanceledException ||
735
+ ex is ObjectDisposedException )
731
736
{
732
737
return ex;
733
738
}
@@ -738,6 +743,23 @@ ex is MongoAuthenticationException ||
738
743
}
739
744
}
740
745
746
+ private Exception WrapInOperationCanceledExceptionIfRequired ( Exception exception , CancellationToken cancellationToken )
747
+ {
748
+ if ( exception is ObjectDisposedException objectDisposedException )
749
+ {
750
+ // We expect two cases here:
751
+ // objectDisposedException.ObjectName == GetType().Name
752
+ // objectDisposedException.Message == "The semaphore has been disposed."
753
+ // but since the last one is language-specific, the only option we have is avoiding any additional conditions for ObjectDisposedException
754
+ // TODO: this logic should be reviewed in the scope of https://jira.mongodb.org/browse/CSHARP-3165
755
+ return new OperationCanceledException( $"The {nameof(BinaryConnection)} operation has been cancelled." , exception ) ;
756
+ }
757
+ else
758
+ {
759
+ return exception;
760
+ }
761
+ }
762
+
741
763
// nested classes
742
764
private class Dropbox
743
765
{
0 commit comments