| File: src\Shared\ProcessSupervisor.Windows.cs | Web Access |
| Project: src\src\Aspire.Cli\Aspire.Cli.csproj (aspire) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; using System.Runtime.InteropServices; using System.Runtime.Versioning; using Microsoft.Win32.SafeHandles; namespace Aspire.Shared; internal static partial class ProcessSupervisor { [SupportedOSPlatform("windows")] private static unsafe SafeWaitHandle CreateWindowsJob() { var handle = CreateJobObjectW(IntPtr.Zero, null); if (handle == IntPtr.Zero) { throw new Win32Exception(Marshal.GetLastPInvokeError(), "Could not create the supervised process job."); } var job = new SafeWaitHandle(handle, ownsHandle: true); var limits = new ExtendedLimitInformation { BasicLimitInformation = new BasicLimitInformation { LimitFlags = 0x2000 } }; // Enroll the guardian before launching the runtime, avoiding the race where // a package-manager lifecycle script creates children before job enrollment. // No breakaway flag: workers must not escape the owner's lifetime. // https://learn.microsoft.com/windows/win32/procthread/job-objects if (!SetInformationJobObject(job, 9, &limits, (uint)sizeof(ExtendedLimitInformation)) || !AssignProcessToJobObject(job, new IntPtr(-1))) { var error = Marshal.GetLastPInvokeError(); job.Dispose(); throw new Win32Exception(error, "Could not contain the process supervisor in its job."); } return job; } [LibraryImport("kernel32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] private static partial IntPtr CreateJobObjectW(IntPtr attributes, string? name); [LibraryImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static partial bool AssignProcessToJobObject(SafeWaitHandle job, IntPtr process); [LibraryImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static unsafe partial bool SetInformationJobObject(SafeWaitHandle job, int informationClass, void* information, uint length); [StructLayout(LayoutKind.Sequential)] private struct BasicLimitInformation { public long PerProcessUserTimeLimit; public long PerJobUserTimeLimit; public uint LimitFlags; public nuint MinimumWorkingSetSize; public nuint MaximumWorkingSetSize; public uint ActiveProcessLimit; public nuint Affinity; public uint PriorityClass; public uint SchedulingClass; } [StructLayout(LayoutKind.Sequential)] private struct IoCounters { public ulong ReadOperationCount; public ulong WriteOperationCount; public ulong OtherOperationCount; public ulong ReadTransferCount; public ulong WriteTransferCount; public ulong OtherTransferCount; } [StructLayout(LayoutKind.Sequential)] private struct ExtendedLimitInformation { public BasicLimitInformation BasicLimitInformation; public IoCounters IoInfo; public nuint ProcessMemoryLimit; public nuint JobMemoryLimit; public nuint PeakProcessMemoryUsed; public nuint PeakJobMemoryUsed; } }