Implement ProcessStartInfo.KillOnParentExit for Windows#126699
Implement ProcessStartInfo.KillOnParentExit for Windows#126699adamsitnik merged 25 commits intomainfrom
Conversation
Add KillOnParentExit property to ProcessStartInfo that terminates child processes when the parent process exits. Windows implementation uses Job Objects with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag. For CreateProcess: uses PROC_THREAD_ATTRIBUTE_JOB_LIST for atomic assignment. For CreateProcessWithLogonW: starts suspended, assigns to job, then resumes. Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/61612ca1-930c-4836-8554-d143d05c8321 Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
adamsitnik
left a comment
There was a problem hiding this comment.
@copilot address my feedback
...ries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Windows.cs
Outdated
Show resolved
Hide resolved
...ries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Windows.cs
Outdated
Show resolved
Hide resolved
...ries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Windows.cs
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/KillOnParentExitTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/KillOnParentExitTests.cs
Outdated
Show resolved
Hide resolved
…py path, add user credentials test, fix pid reporting via stdout, remove nop UseShellExecute test Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/c4dd95b4-cd8e-4740-83f9-7358444c04ce Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
- simplify the tests - avoid duplication!!
There was a problem hiding this comment.
Pull request overview
Implements Windows support for ProcessStartInfo.KillOnParentExit by using Windows Job Objects to ensure spawned processes are terminated when the parent exits.
Changes:
- Added
ProcessStartInfo.KillOnParentExitAPI surface (implementation + ref assembly) and validation againstUseShellExecute. - Implemented Windows job-object based process start paths (CreateProcess job-list attribute, and CreateProcessWithLogonW suspended + assign + resume).
- Added Windows-focused tests validating the behavior.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/libraries/System.Diagnostics.Process/tests/System.Diagnostics.Process.Tests.csproj | Includes the new KillOnParentExit test file in the test project. |
| src/libraries/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs | Extends existing Windows credential tests to cover KillOnParentExit on that path. |
| src/libraries/System.Diagnostics.Process/tests/KillOnParentExitTests.cs | Adds new Windows tests validating KillOnParentExit behavior under various parent-exit scenarios. |
| src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartInfo.cs | Adds the KillOnParentExit property, docs, and validation with UseShellExecute. |
| src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj | Links in the new Windows job-object interop file. |
| src/libraries/System.Diagnostics.Process/src/Resources/Strings.resx | Adds a resource string for the KillOnParentExit + UseShellExecute validation. |
| src/libraries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Windows.cs | Windows implementation: shared job handle, job-list attribute support, and logon path job assignment. |
| src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs | Adds the KillOnParentExit property to the reference assembly surface. |
| src/libraries/Common/src/Interop/Windows/Kernel32/Interop.JobObjects.cs | Adds required job-object P/Invokes/structs/constants. |
| src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ProcessOptions.cs | Adds CREATE_SUSPENDED for the CreateProcessWithLogonW suspended-start path. |
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartInfo.cs
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs
Show resolved
Hide resolved
...ries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Windows.cs
Outdated
Show resolved
Hide resolved
...ries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Windows.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/KillOnParentExitTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/KillOnParentExitTests.cs
Outdated
Show resolved
Hide resolved
This comment has been minimized.
This comment has been minimized.
adamsitnik
left a comment
There was a problem hiding this comment.
@copilot address my feedback
src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs
Show resolved
Hide resolved
...ries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Windows.cs
Outdated
Show resolved
Hide resolved
...ries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Windows.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartInfo.cs
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/KillOnParentExitTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/KillOnParentExitTests.cs
Outdated
Show resolved
Hide resolved
…sumeThread return, remove unused using, improve VerifyProcessIsRunning polling Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/06c58cfa-bdef-4f35-9345-9ab03e7dc542 Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartInfo.cs
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
src/libraries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Windows.cs:524
- If
InitializeProcThreadAttributeList(attributeListBuffer, ...)fails,attributeListBufferis still non-null and thefinallyblock will callDeleteProcThreadAttributeListon a buffer that was never successfully initialized. That’s undefined per Win32 contract and could lead to AVs in low-memory/error paths. Consider only assigning toattributeListBufferafter successful initialization (or track aninitializedflag) so cleanup callsDeleteProcThreadAttributeListonly when initialization succeeded; otherwise just free the allocated memory.
nuint size = 0;
Interop.Kernel32.InitializeProcThreadAttributeList(null, attributeCount, 0, ref size);
attributeListBuffer = NativeMemory.Alloc(size);
if (!Interop.Kernel32.InitializeProcThreadAttributeList(attributeListBuffer, attributeCount, 0, ref size))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
🤖 Copilot Code Review — PR #126699Note This review was AI/Copilot-generated. Holistic AssessmentMotivation: Justified — these are code review follow-up fixes addressing maintainer feedback (jkotas) on the recently-merged Approach: Sound — all changes are incremental improvements that align with dotnet/runtime conventions: SafeHandle-based P/Invoke parameters, correct unsigned return types for Windows DWORD APIs, modern Summary: ✅ LGTM. All changes are correct and well-motivated. No new public API surface. No correctness, safety, or performance concerns found. Reviewed by Claude Opus 4.6 and Claude Sonnet 4.5 (GPT-5.4 also consulted but did not complete in time). Detailed Findings✅ SafeProcessHandle for AssignProcessToJobObject — Correctness improvementChanging ✅ ResumeThread return type and error check — Bug fixThe Windows ✅ ResumeThread in separate file — No duplicate concern
✅ UIntPtr → nuint — Clean modernization
✅ AssignJobAndResumeThread signature — Cleaner interfacePassing only ✅ Test changes — All appropriate
💡 Out-of-scope: Test
|
@copilot Address this feedback by replacing the local ResumeThread with the shared copy |
…d Interop.Kernel32.ResumeThread Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/e8bb36bc-5c71-4723-a32c-17e87d762370 Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
Replaced the local |
…al ResumeThread P/Invoke Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/e8bb36bc-5c71-4723-a32c-17e87d762370 Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/f3703368-dbc7-4226-8258-1bb793d93faf Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
src/libraries/Common/src/Interop/Windows/Kernel32/Interop.JobObjects.cs
Outdated
Show resolved
Hide resolved
...ries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Windows.cs
Outdated
Show resolved
Hide resolved
...ries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Windows.cs
Outdated
Show resolved
Hide resolved
...ries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Windows.cs
Outdated
Show resolved
Hide resolved
…, unnecessary assert, reduce ConfigureExtendedStartupInfo args Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/2dd25b7c-da4a-4ed0-a9b3-cc6561f14aeb Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Description
Implements the
ProcessStartInfo.KillOnParentExitproperty for Windows, which terminates child processes when the parent process exits. This is the Windows part of theKillOnParentExitfeature.API approved at #125838 (comment)
Changes Made
New API surface:
KillOnParentExitproperty toProcessStartInfowith[SupportedOSPlatform("windows")]attribute (linux support will be added in upcoming PRs)ref/System.Diagnostics.Process.cs) with matching[SupportedOSPlatformAttribute("windows")]JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSEWindows implementation (
SafeProcessHandle.Windows.cs):SafeJobHandlewithJOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSEflag, shared across all child processes withKillOnParentExit=trueCreateProcesspath (no user credentials): usesPROC_THREAD_ATTRIBUTE_JOB_LISTin the extended startup info for atomic job assignment at process creationCreateProcessWithLogonWpath (with user credentials): starts the process suspended, assigns it to the job object viaAssignProcessToJobObject, then resumes the thread only on the happy path — on failure, the suspended process is terminatedResumeThreadreturn value is checked; failure throwsWin32Exceptionand the suspended process is terminatedAssignJobAndResumeThreadtakes(IntPtr hThread, SafeProcessHandle procSH)and usesprocSHconsistently for all process handle operations (no mixing ofprocessInfo.hProcessandprocSH)ConfigureExtendedStartupInforeturnsbooland the caller setscreationFlags,startupInfoEx.StartupInfo.cb, andstartupInfoEx.lpAttributeListto reduce the number of method argumentsstring.IsNullOrEmpty(startInfo.UserName)for defensive checks when determining the code pathP/Invoke definitions:
Interop.JobObjects.cs:CreateJobObjectW,SetInformationJobObject,AssignProcessToJobObject(acceptsSafeProcessHandle),JOBOBJECT_EXTENDED_LIMIT_INFORMATION,IO_COUNTERS,JOBOBJECTINFOCLASSstructs/enums,SafeJobHandle,PROC_THREAD_ATTRIBUTE_JOB_LISTconstantInterop.ResumeThread.cs:ResumeThreadP/Invoke returninguint(matching nativeDWORD) in its own dedicated file (following the pattern ofInterop.TerminateProcess.cs,Interop.GetExitCodeProcess.cs, etc.)IO_COUNTERSandJOBOBJECT_EXTENDED_LIMIT_INFORMATIONusenuintfor fields representing numerical values (sizes, limits, affinity masks) per interop conventionResumeThreadP/Invoke inProcessHandlesTests.Windows.cswith the sharedInterop.Kernel32.ResumeThread, eliminating the duplicate declaration and aligning the test code with the productionuint/0xFFFFFFFFpatternValidation:
ProcessStartInfo.ThrowIfInvalidto throwInvalidOperationExceptionwhenKillOnParentExitis combined withUseShellExecuteKillOnParentExitCannotBeUsedWithUseShellExecutestring resourceTests (
KillOnParentExitTests.cs):KillOnParentExit_DefaultsToFalse/KillOnParentExit_CanBeSetToTrue— property basicsKillOnParentExit_WithUseShellExecute_Throws— validation testKillOnParentExit_ProcessStartsAndExitsNormally— smoke testKillOnParentExit_KillsTheChild_WhenParentExits— verifies child is killed (enabled=true) or survives (enabled=false) on normal parent exit, reports grandchild pid via stdoutKillOnParentExit_KillsTheChild_WhenParentIsKilled— same when parent is forcefully killedKillOnParentExit_KillsTheChild_WhenParentCrashes— same when parent crashes (access violation)ProcessStartInfoTests.TestUserCredentialsPropertiesOnWindows(admin/OuterLoop)