Skip to content

Commit

Permalink
Fix a thread pool test
Browse files Browse the repository at this point in the history
- 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 dotnet#66852
  • Loading branch information
kouvel committed Mar 31, 2022
1 parent bd43f55 commit d59a785
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ private uint PerformBlockingAdjustment(bool previousDelayElapsed, out bool addWo

do
{
if (newNumThreadsGoal <= counts.NumExistingThreads)
if (newNumThreadsGoal <= counts.NumExistingThreads || BlockingConfig.IgnoreMemoryUsage)
{
break;
}
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
18 changes: 10 additions & 8 deletions src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);

Expand Down Expand Up @@ -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();
}
Expand Down

0 comments on commit d59a785

Please sign in to comment.