in src/Microsoft.Diagnostics.Runtime/ClrThreadPool.cs [82:155]
internal ClrThreadPool(ClrRuntime runtime, IAbstractLegacyThreadPool? helpers)
{
_runtime = runtime;
_legacyData = helpers;
LegacyThreadPoolInfo tpData = default;
HasLegacyData = _legacyData is not null && _legacyData.GetLegacyThreadPoolData(out tpData);
ClrAppDomain domain = GetDomain();
GetPortableOrWindowsThreadPoolInfo(domain,
out bool usingPortableThreadPool,
out ClrObject portableThreadPool,
out bool usingWindowsThreadPool,
out ClrType? windowsThreadPoolType);
UsingPortableThreadPool = usingPortableThreadPool;
UsingWindowsThreadPool = usingWindowsThreadPool;
Initialized = UsingPortableThreadPool || UsingWindowsThreadPool || HasLegacyData;
if (UsingWindowsThreadPool)
{
ClrStaticField threadCountField = windowsThreadPoolType!.GetStaticFieldByName("s_threadCount")!;
WindowsThreadPoolThreadCount = threadCountField.Read<int>(domain);
return;
}
if (UsingPortableThreadPool)
{
CpuUtilization = portableThreadPool.ReadField<int>("_cpuUtilization");
MinThreads = portableThreadPool.ReadField<ushort>("_minThreads");
MaxThreads = portableThreadPool.ReadField<ushort>("_maxThreads");
ClrValueType counts = portableThreadPool.ReadValueTypeField("_separated").ReadValueTypeField("counts").ReadValueTypeField("_data");
ulong dataValue = counts.ReadField<ulong>("m_value");
int processingWorkCount = (ushort)(dataValue & 0xffff);
int existingThreadCount = (ushort)((dataValue >> 16) & 0xffff);
IdleWorkerThreads = existingThreadCount - processingWorkCount;
ActiveWorkerThreads = processingWorkCount;
RetiredWorkerThreads = 0;
}
else if (HasLegacyData)
{
CpuUtilization = tpData.CpuUtilization;
MinThreads = tpData.MinLimitTotalWorkerThreads;
MaxThreads = tpData.MaxLimitTotalWorkerThreads;
IdleWorkerThreads = tpData.NumIdleWorkerThreads;
ActiveWorkerThreads = tpData.NumWorkingWorkerThreads;
RetiredWorkerThreads = tpData.NumRetiredWorkerThreads;
_nativeLogAddress = tpData.HillClimbingLog;
_nativeLogStart = tpData.HillClimbingLogFirstIndex;
_nativeLogSize = tpData.HillClimbingLogSize;
_firstLegacyWorkRequest = tpData.FirstUnmanagedWorkRequest;
_asyncTimerFunction = tpData.AsyncTimerCallbackCompletionFPtr;
}
// The legacy IO completion thread pool may also be used while the portable thread pool is being used for worker threads
if (HasLegacyData)
{
TotalCompletionPorts = tpData.NumCPThreads;
FreeCompletionPorts = tpData.NumFreeCPThreads;
MaxFreeCompletionPorts = tpData.MaxFreeCPThreads;
CompletionPortCurrentLimit = tpData.CurrentLimitTotalCPThreads;
MaxCompletionPorts = tpData.MaxLimitTotalCPThreads;
MinCompletionPorts = tpData.MinLimitTotalCPThreads;
}
}