Add ProcessStartInfo.StartDetached to start processes detached from parent session/console#126632
Add ProcessStartInfo.StartDetached to start processes detached from parent session/console#126632adamsitnik merged 15 commits intomainfrom
Conversation
|
Tagging subscribers to this area: @dotnet/area-system-diagnostics-process |
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/23306a05-946d-446f-b7b5-f7cfc977422c 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
src/libraries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.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/src/System/Diagnostics/ProcessStartInfo.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/StartDetachedTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/StartDetachedTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/StartDetachedTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/StartDetachedTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/StartDetachedTests.cs
Outdated
Show resolved
Hide resolved
…dition, test attributes, and pal_process nesting Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/59c890e4-010d-41ee-9d51-bcd6e64cb7f3 Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
All feedback addressed in a6e4206. Summary of changes:
|
There was a problem hiding this comment.
Pull request overview
This PR introduces a new ProcessStartInfo.StartDetached API to start processes detached from the parent’s session/console across platforms, with additional safety defaults to avoid leaking standard handles and inheritable resources into long-lived detached processes.
Changes:
- Adds
ProcessStartInfo.StartDetachedto the public surface area (ref + implementation) with validation againstUseShellExecute = true. - Implements platform-specific detaching:
DETACHED_PROCESS(Windows),POSIX_SPAWN_SETSID(macOS), andsetsid()(other Unix), threading the flag through managed/native interop. - When detached, defaults stdio to the null device (unless explicitly configured) and restricts handle inheritance by default.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/native/libs/System.Native/pal_process.h | Extends native fork/exec API with startDetached parameter. |
| src/native/libs/System.Native/pal_process.c | Implements session detachment (POSIX_SPAWN_SETSID / setsid()) based on startDetached. |
| src/native/libs/System.Native/pal_process_wasi.c | Updates WASI stub signature for startDetached. |
| src/libraries/Common/src/Interop/Unix/System.Native/Interop.ForkAndExecProcess.cs | Adds managed interop parameter and forwards startDetached to native. |
| src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ProcessOptions.cs | Introduces DETACHED_PROCESS constant for process creation flags. |
| src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs | Adds ProcessStartInfo.StartDetached to the reference assembly API surface. |
| src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartInfo.cs | Adds property + validation + default empty inherited-handles list for detached starts. |
| src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs | Uses a single null-device handle for unset stdio when detached; opens it outside the lock. |
| src/libraries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.cs | Applies detached stdio-to-null behavior for SafeProcessHandle.Start. |
| src/libraries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Unix.cs | Threads StartDetached through to Interop.Sys.ForkAndExecProcess. |
| src/libraries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Windows.cs | Adds DETACHED_PROCESS to creation flags when StartDetached is set. |
| src/libraries/System.Diagnostics.Process/src/Resources/Strings.resx | Adds error message for StartDetached + UseShellExecute incompatibility. |
| src/libraries/System.Diagnostics.Process/tests/System.Diagnostics.Process.Tests.csproj | Includes new StartDetachedTests.cs in the test project. |
| src/libraries/System.Diagnostics.Process/tests/StartDetachedTests.cs | Adds validation and behavioral tests for detached starts (including a Unix SIGHUP scenario). |
src/libraries/System.Diagnostics.Process/tests/StartDetachedTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartInfo.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartInfo.cs
Outdated
Show resolved
Hide resolved
src/libraries/Common/src/Interop/Unix/System.Native/Interop.ForkAndExecProcess.cs
Show resolved
Hide resolved
This comment has been minimized.
This comment has been minimized.
ac41ee9 to
fbd8320
Compare
This comment has been minimized.
This comment has been minimized.
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartInfo.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartInfo.cs
Outdated
Show resolved
Hide resolved
adamsitnik
left a comment
There was a problem hiding this comment.
@copilot address my feedback
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartInfo.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartInfo.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/src/Resources/Strings.resx
Outdated
Show resolved
Hide resolved
…link to docs - Per @jkotas: DETACHED_PROCESS and CREATE_NO_WINDOW can coexist without error, so remove the CreateNoWindow guard from ThrowIfInvalid - Update StartDetachedNotCompatible message to only mention UseShellExecute - Update XML docs: remove CreateNoWindow mention; add href to Windows SDK docs for DETACHED_PROCESS (process-creation-flags#flags) - Remove the now-obsolete StartDetached_ThrowsOnCreateNoWindow test Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/d92ad4f1-6749-4044-bfe0-43cc565d1ad9 Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Note This review was generated by Copilot AI using three parallel models (Claude Sonnet 4.5, Gemini 2.5 Pro, GPT-4.1) and synthesized into a single report. 🤖 Copilot Code Review — PR #126632Holistic AssessmentMotivation: Well-justified. A cross-platform API for starting daemon/background processes is a genuine gap — today users must resort to platform-specific flags or shell scripting. Issue #125838 carries the Approach: Correct on all three platforms: Summary: ✅ LGTM. All three models independently raised two concerns that, after careful verification, are false positives: Detailed Findings✅ Handle disposal in
|
…led to avoid PID reuse issues
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartInfo.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartInfo.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: Jan Kotas <jkotas@microsoft.com> Co-authored-by: Adam Sitnik <adam.sitnik@gmail.com>
🤖 Copilot Code Review — PR #126632Note This review was generated by GitHub Copilot (Claude Opus 4.6). Full source files, git history, API approval issue, and existing review comments were analyzed. Holistic AssessmentMotivation: Adding Approach: The implementation correctly uses platform-appropriate mechanisms ( Summary: Detailed Findings✅ API Approval — Verified against issue #125838The public sealed partial class ProcessStartInfo
{
public bool StartDetached { get; set; }
}The implementation matches exactly — no extra public surface, correct property signature, correct placement in alphabetical order in the ref assembly (after ✅ Native Implementation — Correct platform mechanisms
✅ Windows Implementation — Correct flag usage
✅ Validation Logic — Correct and complete
|
|
/ba-g unrelated |
Description
Adds
ProcessStartInfo.StartDetached— a cross-platform way to start a process detached from the parent's session and console, such that it outlives the parent.API surface
Behavior by platform
DETACHED_PROCESScreation flagPOSIX_SPAWN_SETSIDvia posix_spawnsetsid()in child after fork, before execKey semantics
InvalidOperationExceptionwhen combined withUseShellExecute = trueNULon Windows,/dev/nullon Unix) — a singleFile.OpenNullHandle()is opened at most once and disposed after the process starts; override withRedirectStandardInput/RedirectStandardOutput/RedirectStandardErrororStandardInputHandle/StandardOutputHandle/StandardErrorHandleExample
Files changed
ProcessStartInfo.cs/ ref — new property with XML docs linking to the Windows SDKDETACHED_PROCESSdocumentation; remarks note thatUseShellExecute = truewill throw;StartDetachedplaced in alphabetical order in the ref assembly (afterStandardOutputHandle, beforeUseCredentialsForNetworkingOnly)Strings.resx— resource keyStartDetachedNotCompatible; message states the property cannot be used withUseShellExecuteset totrueProcess.cs— opens a single null device handle outside the lock region, reuses it for all unset stdio handles; existingfinallyblock handles disposalSafeProcessHandle.cs— if/else if/else blocks; singlenullDeviceHandleopened at most once; disposed in afinallyblock afterStartCoreSafeProcessHandle.Windows.cs—DETACHED_PROCESSflag; constant added toInterop.ProcessOptions.csInterop.ForkAndExecProcess.cs/SafeProcessHandle.Unix.cs— threadstartDetachedthrough managed interop chain; fixed indentation ofForkAndExecProcess(...)call insidefixedblockpal_process.c/pal_process.h/pal_process_wasi.c— native implementation:POSIX_SPAWN_SETSID(macOS),if (startDetached && setsid() == -1)check (other Unix)ProcessTests.cs— cross-platformStartDetached_GrandchildSurvivesSignalingParenttest guarded byIsNotNanoServerAndRemoteExecutorSupported(PlatformDetection.IsNotWindowsNanoServer && RemoteExecutor.IsSupported); child started withStartDetached=true(own process group on Unix) orCreateNewProcessGroup=true(Windows); kills child's process group withSIGKILLon Unix andSIGQUIT(mapped toCTRL_BREAK_EVENT) on Windows viaSendSignal(..., entireProcessGroup: true); grandchild-alive check uses deterministic polling (loop with 20 ms sleep up toWaitInMS) instead of a fixedThread.Sleep; validates that detached grandchild survives while non-detached grandchild is killed;StartDetached_StartsAndExitsSuccessfullysmoke test starts a process withStartDetached=trueviaRemoteExecutorand verifies it completes successfullyProcessTests.Unix.cs—SendSignal(..., entireProcessGroup: true)passes the given signal throughInterop.Sys.GetPlatformSignalNumbertokill(-pgid, signalNumber), keeping the helper generic (not hardcoded to any particular signal); call site passesPosixSignal.SIGKILLon Unix for reliable process-group terminationProcessTests.Windows.cs—SendSignaldocuments thatentireProcessGrouphas no distinct effect on Windows (via_ = entireProcessGroup) sinceGenerateConsoleCtrlEventis inherently process-group scopedProcessHandlesTests.cs—[Fact]test forUseShellExecutevalidationChanges
ProcessStartInfo.StartDetachedpropertyDETACHED_PROCESS(Windows),POSIX_SPAWN_SETSID(macOS),setsid()(Linux/other Unix)UseShellExecute = trueonly (CreateNoWindowis not rejected —DETACHED_PROCESSandCREATE_NO_WINDOWare compatible per Windows API semantics)DETACHED_PROCESSdocumentation; onlyUseShellExecutelisted as incompatibleUseShellExecute), cross-platform process-group isolation (detached grandchild survives, non-detached grandchild is killed), and basic smoke test (StartDetached_StartsAndExitsSuccessfully); all skipped on Windows Nano Server and whereRemoteExecutoris unsupportedSendSignalUnix helper kept generic (signal passed viaGetPlatformSignalNumber); call site usesSIGKILLon Unix for reliable group terminationThread.Sleep(200)in grandchild-survival assertionSendSignalexplicitly discardsentireProcessGroupwith a clarifying comment thatGenerateConsoleCtrlEventis inherently group-scopedStartDetachedplaced in alphabetical order in the ref assembly (afterStandardOutputHandle, beforeUseCredentialsForNetworkingOnly)