| File: Windows\WindowsContainerResourceQuotaProvider.cs | Web Access |
| Project: src\src\Libraries\Microsoft.Extensions.Diagnostics.ResourceMonitoring\Microsoft.Extensions.Diagnostics.ResourceMonitoring.csproj (Microsoft.Extensions.Diagnostics.ResourceMonitoring) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Threading; using Microsoft.Extensions.Diagnostics.ResourceMonitoring.Windows.Interop; namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Windows; internal sealed class WindowsContainerResourceQuotaProvider : ResourceQuotaProvider { private readonly ISystemInfo _systemInfo; private readonly Lazy<MEMORYSTATUSEX> _memoryStatus; private Func<IJobHandle> _createJobHandleObject; public WindowsContainerResourceQuotaProvider() : this(new MemoryInfo(), new SystemInfo(), static () => new JobHandleWrapper()) { } public WindowsContainerResourceQuotaProvider(IMemoryInfo memoryInfo, ISystemInfo systemInfo, Func<IJobHandle> createJobHandleObject) { _systemInfo = systemInfo; _createJobHandleObject = createJobHandleObject; _memoryStatus = new Lazy<MEMORYSTATUSEX>( memoryInfo.GetMemoryStatus, LazyThreadSafetyMode.ExecutionAndPublication); } public override ResourceQuota GetResourceQuota() { // bring logic from WindowsContainerSnapshotProvider for limits and requests using IJobHandle jobHandle = _createJobHandleObject(); var resourceQuota = new ResourceQuota { MaxCpuInCores = GetCpuLimit(jobHandle, _systemInfo), MaxMemoryInBytes = GetMemoryLimit(jobHandle), }; // CPU guaranteed (aka minimum CPU units) is not supported on Windows, so we set it to the same value as CPU maximum (aka limit CPU units). // Memory guaranteed (aka minimum memory) is not supported on Windows, so we set it to the same value as memory maximum (aka limit memory). resourceQuota.BaselineCpuInCores = resourceQuota.MaxCpuInCores; resourceQuota.BaselineMemoryInBytes = resourceQuota.MaxMemoryInBytes; return resourceQuota; } private static double GetCpuLimit(IJobHandle jobHandle, ISystemInfo systemInfo) { // Note: This function convert the CpuRate from CPU cycles to CPU units, also it scales // the CPU units with the number of processors (cores) available in the system. const double CpuCycles = 10_000U; var cpuLimit = jobHandle.GetJobCpuLimitInfo(); double cpuRatio = 1.0; if ((cpuLimit.ControlFlags & (uint)JobObjectInfo.JobCpuRateControlLimit.CpuRateControlEnable) != 0 && (cpuLimit.ControlFlags & (uint)JobObjectInfo.JobCpuRateControlLimit.CpuRateControlHardCap) != 0) { // The CpuRate is represented as number of cycles during scheduling interval, where // a full cpu cycles number would equal 10_000, so for example if the CpuRate is 2_000, // that means that the application (or container) is assigned 20% of the total CPU available. // So, here we divide the CpuRate by 10_000 to convert it to a ratio (ex: 0.2 for 20% CPU). // For more info: https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_cpu_rate_control_information?redirectedfrom=MSDN cpuRatio = cpuLimit.CpuRate / CpuCycles; } SYSTEM_INFO systemInfoValue = systemInfo.GetSystemInfo(); // Multiply the cpu ratio by the number of processors to get you the portion // of processors used from the system. return cpuRatio * systemInfoValue.NumberOfProcessors; } /// <summary> /// Gets memory limit of the system. /// </summary> /// <returns>Memory limit allocated to the system in bytes.</returns> private ulong GetMemoryLimit(IJobHandle jobHandle) { var memoryLimitInBytes = jobHandle.GetExtendedLimitInfo().JobMemoryLimit.ToUInt64(); if (memoryLimitInBytes <= 0) { MEMORYSTATUSEX memoryStatus = _memoryStatus.Value; // Technically, the unconstrained limit is memoryStatus.TotalPageFile. // Leaving this at physical as it is more understandable to consumers. memoryLimitInBytes = memoryStatus.TotalPhys; } return memoryLimitInBytes; } }