@@ -51,12 +51,6 @@ internal sealed class HttpConnectionPoolManager : IDisposable
5151 /// <see cref="ConcurrentDictionary{TKey,TValue}.IsEmpty"/> call.
5252 /// </summary>
5353 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-
6054 /// <summary>Object used to synchronize access to state in the pool.</summary>
6155 private object SyncObj => _pools ;
6256
@@ -468,7 +462,7 @@ private void SetCleaningTimer(TimeSpan timeout)
468462 {
469463 try
470464 {
471- _cleaningTimer ! . Change ( timeout , timeout ) ;
465+ _cleaningTimer ! . Change ( timeout , Timeout . InfiniteTimeSpan ) ;
472466 _timerIsRunning = timeout != Timeout . InfiniteTimeSpan ;
473467 }
474468 catch ( ObjectDisposedException )
@@ -485,40 +479,23 @@ private void RemoveStalePools()
485479 {
486480 Debug . Assert ( _cleaningTimer != null ) ;
487481
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 )
495488 {
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 ( ) )
502490 {
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 _ ) ;
516492 }
517493 }
518- finally
494+
495+ // Restart the timer if we have any pools to clean up.
496+ lock ( SyncObj )
519497 {
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 ) ;
522499 }
523500
524501 // NOTE: There is a possible race condition with regards to a pool getting cleaned up at the same
0 commit comments