From d59a78521c8915dcf1a8fbf5ad9394d75af3d1a5 Mon Sep 17 00:00:00 2001 From: Koundinya Veluri Date: Thu, 31 Mar 2022 12:41:17 -0700 Subject: [PATCH] Fix a thread pool test - The test verifies that cooperative blocking in `Task.Wait()` causes threads to be injected quickly enough that it does not time out due to starvation - Cooperative blocking checks memory usage and limit, and beyond a threshold of memory usage does not inject threads quickly - Following a build of the runtime repo, there are several `dotnet` processes that remain running, each using several 100s of MBs and one using ~1.3 GB of memory - If the test runs soon after the build, before those processes exit, it's possible for the reported memory usage to be high enough that the test would fail - Added a config var to ignore memory usage and used it in the test Fixes https://github.com/dotnet/runtime/issues/66852 --- .../Threading/PortableThreadPool.Blocking.cs | 4 +++- .../Threading/PortableThreadPool.GateThread.cs | 2 +- .../tests/ThreadPoolTests.cs | 18 ++++++++++-------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Blocking.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Blocking.cs index bf2b0d1d1cb3c7..73fe2afe9a4d0e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Blocking.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Blocking.cs @@ -180,7 +180,7 @@ private uint PerformBlockingAdjustment(bool previousDelayElapsed, out bool addWo do { - if (newNumThreadsGoal <= counts.NumExistingThreads) + if (newNumThreadsGoal <= counts.NumExistingThreads || BlockingConfig.IgnoreMemoryUsage) { break; } @@ -260,6 +260,8 @@ private static class BlockingConfig { public static readonly bool IsCooperativeBlockingEnabled = AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.Blocking.CooperativeBlocking", true); + public static readonly bool IgnoreMemoryUsage = + AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.Blocking.IgnoreMemoryUsage", false); public static readonly short ThreadsToAddWithoutDelay; public static readonly short ThreadsPerDelayStep; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.GateThread.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.GateThread.cs index b28286d8ebe0e5..2843f99347ca50 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.GateThread.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.GateThread.cs @@ -36,7 +36,7 @@ private static void GateThreadStart() LowLevelLock threadAdjustmentLock = threadPoolInstance._threadAdjustmentLock; DelayHelper delayHelper = default; - if (BlockingConfig.IsCooperativeBlockingEnabled) + if (BlockingConfig.IsCooperativeBlockingEnabled && !BlockingConfig.IgnoreMemoryUsage) { // Initialize memory usage and limits, and register to update them on gen 2 GCs threadPoolInstance.OnGen2GCCallback(); diff --git a/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs b/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs index 2c380f9d401b77..71cfdc57a87093 100644 --- a/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs +++ b/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs @@ -906,7 +906,6 @@ public static void ThreadPoolThreadCreationDoesNotTransferExecutionContext() } [ConditionalFact(nameof(IsThreadingAndRemoteExecutorSupported))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/66852", TestPlatforms.OSX)] public static void CooperativeBlockingCanCreateThreadsFaster() { // Run in a separate process to test in a clean thread pool environment such that work items queued by the test @@ -924,6 +923,7 @@ public static void CooperativeBlockingCanCreateThreadsFaster() int workItemCount = processorCount + 120; SetBlockingConfigValue("ThreadsToAddWithoutDelay_ProcCountFactor", 1); SetBlockingConfigValue("MaxDelayMs", 1); + SetBlockingConfigValue("IgnoreMemoryUsage", true); var allWorkItemsUnblocked = new AutoResetEvent(false); @@ -954,17 +954,19 @@ public static void CooperativeBlockingCanCreateThreadsFaster() Assert.True(allWorkItemsUnblocked.WaitOne(30_000)); } - void SetBlockingConfigValue(string name, int value) => + void SetBlockingConfigValue(string name, object value) => AppContextSetData("System.Threading.ThreadPool.Blocking." + name, value); void AppContextSetData(string name, object value) { - typeof(AppContext).InvokeMember( - "SetData", - BindingFlags.ExactBinding | BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, - null, - null, - new object[] { name, value }); + if (value is bool boolValue) + { + AppContext.SetSwitch(name, boolValue); + } + else + { + AppContext.SetData(name, value); + } } }).Dispose(); }