@@ -51,12 +51,6 @@ internal sealed class HttpConnectionPoolManager : IDisposable
51
51
/// <see cref="ConcurrentDictionary{TKey,TValue}.IsEmpty"/> call.
52
52
/// </summary>
53
53
private bool _timerIsRunning ;
54
-
55
- /// <summary>
56
- /// Prevents parallel execution of RemoveStalePools in case the timer triggers faster than the method itself finishes.
57
- /// </summary>
58
- private int _removeStalePoolsIsRunning ;
59
-
60
54
/// <summary>Object used to synchronize access to state in the pool.</summary>
61
55
private object SyncObj => _pools ;
62
56
@@ -468,7 +462,7 @@ private void SetCleaningTimer(TimeSpan timeout)
468
462
{
469
463
try
470
464
{
471
- _cleaningTimer ! . Change ( timeout , timeout ) ;
465
+ _cleaningTimer ! . Change ( timeout , Timeout . InfiniteTimeSpan ) ;
472
466
_timerIsRunning = timeout != Timeout . InfiniteTimeSpan ;
473
467
}
474
468
catch ( ObjectDisposedException )
@@ -485,40 +479,23 @@ private void RemoveStalePools()
485
479
{
486
480
Debug . Assert ( _cleaningTimer != null ) ;
487
481
488
- // Check whether the method is not already running and prevent parallel execution.
489
- if ( Interlocked . CompareExchange ( ref _removeStalePoolsIsRunning , 1 , 0 ) != 0 )
490
- {
491
- return ;
492
- }
493
-
494
- try
482
+ // Iterate through each pool in the set of pools. For each, ask it to clear out
483
+ // any unusable connections (e.g. those which have expired, those which have been closed, etc.)
484
+ // The pool may detect that it's empty and long unused, in which case it'll dispose of itself,
485
+ // such that any connections returned to the pool to be cached will be disposed of. In such
486
+ // a case, we also remove the pool from the set of pools to avoid a leak.
487
+ foreach ( KeyValuePair < HttpConnectionKey , HttpConnectionPool > entry in _pools )
495
488
{
496
- // Iterate through each pool in the set of pools. For each, ask it to clear out
497
- // any unusable connections (e.g. those which have expired, those which have been closed, etc.)
498
- // The pool may detect that it's empty and long unused, in which case it'll dispose of itself,
499
- // such that any connections returned to the pool to be cached will be disposed of. In such
500
- // a case, we also remove the pool from the set of pools to avoid a leak.
501
- foreach ( KeyValuePair < HttpConnectionKey , HttpConnectionPool > entry in _pools )
489
+ if ( entry . Value . CleanCacheAndDisposeIfUnused ( ) )
502
490
{
503
- if ( entry . Value . CleanCacheAndDisposeIfUnused ( ) )
504
- {
505
- _pools . TryRemove ( entry . Key , out HttpConnectionPool _ ) ;
506
- }
507
- }
508
-
509
- // Stop running the timer if we don't have any pools to clean up.
510
- lock ( SyncObj )
511
- {
512
- if ( _pools . IsEmpty )
513
- {
514
- SetCleaningTimer ( Timeout . InfiniteTimeSpan ) ;
515
- }
491
+ _pools . TryRemove ( entry . Key , out HttpConnectionPool _ ) ;
516
492
}
517
493
}
518
- finally
494
+
495
+ // Restart the timer if we have any pools to clean up.
496
+ lock ( SyncObj )
519
497
{
520
- // Make sure the guard value gets always reset back to 0 and that it's visible to other threads.
521
- Volatile . Write ( ref _removeStalePoolsIsRunning , 0 ) ;
498
+ SetCleaningTimer ( ! _pools . IsEmpty ? _cleanPoolTimeout : Timeout . InfiniteTimeSpan ) ;
522
499
}
523
500
524
501
// NOTE: There is a possible race condition with regards to a pool getting cleaned up at the same
0 commit comments