[release/6.0] [HTTP] Scavenge fix (#62008)
authorgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Wed, 15 Dec 2021 18:39:20 +0000 (12:39 -0600)
committerGitHub <noreply@github.com>
Wed, 15 Dec 2021 18:39:20 +0000 (12:39 -0600)
* Guard pool scavenging callback from parallel execution when it takes longer than the timer interval gets triggered

* Dispose connection from the pool in a serate task to not to block the caller (scavenge timer callback)

* feedback

Co-authored-by: ManickaP <mapichov@microsoft.com>
src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs
src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPoolManager.cs

index 2879d37..75b83fb 100644 (file)
@@ -1979,7 +1979,12 @@ namespace System.Net.Http
             }
 
             // Dispose the stale connections outside the pool lock, to avoid holding the lock too long.
-            toDispose?.ForEach(c => c.Dispose());
+            // Dispose them asynchronously to not to block the caller on closing the SslStream or NetworkStream.
+            if (toDispose is not null)
+            {
+                Task.Factory.StartNew(static s => ((List<HttpConnectionBase>)s!).ForEach(c => c.Dispose()), toDispose,
+                    CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+            }
 
             // Pool is active.  Should not be removed.
             return false;
index e257cb4..f118d15 100644 (file)
@@ -462,7 +462,7 @@ namespace System.Net.Http
         {
             try
             {
-                _cleaningTimer!.Change(timeout, timeout);
+                _cleaningTimer!.Change(timeout, Timeout.InfiniteTimeSpan);
                 _timerIsRunning = timeout != Timeout.InfiniteTimeSpan;
             }
             catch (ObjectDisposedException)
@@ -492,13 +492,10 @@ namespace System.Net.Http
                 }
             }
 
-            // Stop running the timer if we don't have any pools to clean up.
+            // Restart the timer if we have any pools to clean up.
             lock (SyncObj)
             {
-                if (_pools.IsEmpty)
-                {
-                    SetCleaningTimer(Timeout.InfiniteTimeSpan);
-                }
+                SetCleaningTimer(!_pools.IsEmpty ? _cleanPoolTimeout : Timeout.InfiniteTimeSpan);
             }
 
             // NOTE: There is a possible race condition with regards to a pool getting cleaned up at the same