Repository navigation
[Process]: Start detached process #124334
Description
Activity
- addedapi-ready-for-reviewAPI is ready for review, it is NOT ready for implementationAPI is ready for review, it is NOT ready for implementation
on Feb 12, 2026 cc @davidfowl
Reacted by David Fowler and Sébastien Rosdotnet-policy-service commented
on Feb 12, 2026 ContributorMore actionsTagging subscribers to this area: @dotnet/area-system-diagnostics-process
See info in area-owners.md if you want to be subscribed.Should StartSuspended, StartDetached, ... really be separate methods? It feels like these should be properties in ProcessStartOptions, so you can e.g. start a process that is both suspended and detached.
Should StartSuspended, StartDetached, ... really be separate methods? It feels like these should be properties in ProcessStartOptions, so you can e.g. start a process that is both suspended and detached.
Great question!
This was my initial design, but when I started providing the high-level helpers for most common-scenarios (#123959), I realized that each of these helpers would need to check if the option bag does not request a
Suspendedprocess and just throw in such case.ProcessStartOptions info = new("dotnet") { Arguments = { "restore" } }; ProcessOutput processOutput = ChildProcess.CaptureOutput(info);
Moreover, exposing these APIs as part of the option bag would "pollute" the API surface for the 99% of users who don't need to support these two niche (but valid and IMO important) scenarios.
Moreover, exposing these APIs as part of the option bag would "pollute" the API surface for the 99% of users who don't need to support these two niche (but valid and IMO important) scenarios.
ProcessStartOptions has other very niche features like InheritedHandles that 99% of users won't ever use. I think we struggling with deciding whether ProcessStartOptions should or should not have the niche features. It is a mix right now that leads to poor overall design.
Moreover, exposing these APIs as part of the option bag would "pollute" the API surface for the 99% of users who don't need to support these two niche (but valid and IMO important) scenarios.
ProcessStartOptions has other very niche features like InheritedHandles that 99% of users won't ever use. I think we struggling with deciding whether ProcessStartOptions should or should not have the niche features. It is a mix right now that leads to poor overall design.
I agree
InheritedHandlesare niche, but I can see some valid scenarios where they would be useful in high level helpers.FWIW I've mentioned this in the API proposal: #123380

I agree InheritedHandles are niche, but I can see some valid scenarios where they would be useful in high level helpers.
Right, you can always find a case where somebody might want to use a niche feature together with high level helpers.
I agree InheritedHandles are niche, but I can see some valid scenarios where they would be useful in high level helpers.
Right, you can always find a case where somebody might want to use a niche feature together with high level helpers.
I was not sure myself, that is why I've mentioned this in the API proposal. I was hoping that we discuss it during the API review. Should I discuss it with API review board again? For example along with the current issue (StartDetached)?
In general, the goal of API review isn't to question fundamental assertions about a feature being needed (though we often end up discussing that); rather, it's assumed that the area owners are asserting that such functionality is necessary, and then the discussion is about the right way to expose it.
Should I discuss it with API review board again? For example along with the current issue (StartDetached)?
I think StartSuspended should be re-reviewed together with StartDetached. StartSuspended + StartDetached is not composable pattern. I think it is flawed design. API composability is more important than whether we need to have an extra check in the implementation or whether somebody will see an extra property in IntelliSense.
Reacted by Michał Petryka, Matt Zinkevicius, John Tur and antiufoI think StartSuspended should be re-reviewed together with StartDetached. StartSuspended + StartDetached is not composable pattern. I think it is flawed design. API composability is more important than whether we need to have an extra check in the implementation or whether somebody will see an extra property in IntelliSense.
StartDetachedis very specific, as it must not inherit any handles (the detached process could live way longer than the parent and hold plenty of resources opened, there are some security aspects involved as well).I agree that composability is very important, but I don't believe that we should expose two additional boolean properties that would be used by 0.01% of the users (and most likely almost never used together) at the cost of polluting the option bag api surface for 99.99% of other users. I really want to avoid the situation where people want to try to use
ProcessStartOptionsfor the first time and they thinks it's just anotherProcessStartInfowith a lipstick on a pig.Let's bring it to the API review and discuss in detail.
7 remaining items
for features specific to Linux/macOS, add them to UnixProcessStartOption but annotate with SupportedOSPlatform.
It looks odd to me to separate Windows and Unix, and lump Linux and macOS together. If we are not doing specific types for every OS, I think we should go with one type for all advances options.
The problem is that we can't do that in .NET, because this logic is executed in the child process and we can't execute any managed code in the child process because there is no JIT to or GC.
We can expose this callback and require people to implement this callback in unmanaged code. We have existing APIs like that. For example,. callbacks provided for
ObjectiveCMarshal.Initializemust be implemented in unmanaged code.This works cross-platform
Cross-platform does not mean latest Windows/Linux/macOS only. Does it really well work cross-platform, including other Unix flavors? I think you need specialized syscalls like Linux
close_rangeto make it work well.It looks odd to me to separate Windows and Unix, and lump Linux and macOS together. If we are not doing specific types for every OS, I think we should go with one type for all advances options.
I agree, it's definitely not a perfect solution. I would love to hear more from others.
Cross-platform does not mean latest Windows/Linux/macOS only. Does it really well work cross-platform, including other Unix flavors? I think you need specialized syscalls like Linux
close_rangeto make it work well.It does, because the goal of this API is to ensure that the new process is going to derive the provided handles. When it comes to not deriving other handles, it's best effort attempt (works on every macOS, modern Linux, modern Android and modern FreeBSD) and will be documented as such. FWIW all modern programming languages (golang, rust) do that already.
We can expose this callback and require people to implement this callback in unmanaged code. We have existing APIs like that. For example,. callbacks provided for
ObjectiveCMarshal.Initializemust be implemented in unmanaged code.For my education. how
delegate* unmanagedworks exactly? If we have code like this:static unsafe void Main() { delegate* unmanaged<void> del1 = &RequireUnreferencedCode; } [UnmanagedCallersOnly(EntryPoint = nameof(RequireUnreferencedCode))] static void RequireUnreferencedCode() { }
Can we somehow ensure that
RequireUnreferencedCodeis pre-compiled and invoking it does not introduce any JIT or allocations? Or do we somehow need to load the native library and get a reference to its method?Can we somehow ensure that RequireUnreferencedCode is pre-compiled and invoking it does not introduce any JIT or allocations?
We can ensure that we will crash deterministically when you run your code snippet.
it does not introduce any JIT or allocations
There is no way to write a code like that in C# today. When I said unmanaged code, I meant unmanaged code (like C/C++).
The discussion on this made us seem like we're already regretting ProcessStartOptions, and maybe it should go back to ProcessStartInfo and be rethought from there.
- addedapi-needs-workAPI needs work before it is approved, it is NOT ready for implementationAPI needs work before it is approved, it is NOT ready for implementationand removedapi-ready-for-reviewAPI is ready for review, it is NOT ready for implementationAPI is ready for review, it is NOT ready for implementation
on Mar 17, 2026 - removedapi-needs-workAPI needs work before it is approved, it is NOT ready for implementationAPI needs work before it is approved, it is NOT ready for implementation
on Mar 25, 2026 Update: the API got approved in #125838 (comment)
It's blocked by #13943
Coming soon (famous last words)
Reacted by Addison YatesReacted by artdedecoHey folks, I was reading up on this and I'm a bit confused from following the discussion so far; is the intention of this new API to have
StartDetachedstart the new process such that its lifetime is independent of the parent process, in a cross-platform way? The docs for the property in the OP suggests the answer is yes, but from reading the discussion I'm not sure.is the intention of this new API to have
StartDetachedstart the new process such that its lifetime is independent of the parent process, in a cross-platform way?Yes, this is the intention.
Reacted by artdedeco and Matt Parker- linked a pull request that will close this issueAdd ProcessStartInfo.StartDetached to start processes detached from parent session/console #126632
on Apr 8, 2026 - locked and limited conversation to collaborators
on May 11, 2026
Background and motivation
There is currently no easy, cross-platform way to start a detached process in .NET. A detached process is one that:
This is a common requirement for scenarios such as:
Currently, achieving this requires platform-specific workarounds like invoking shell commands like
nohup <command> &through/bin/sh, which requires concatenating arguments as strings and loses the ability to obtain the child process ID (see #104210)These workarounds are error-prone, not portable, and don't provide consistent behavior across platforms.
API Proposal
The proposal is to extend
ProcessStartOptionswith a newStartDetachedproperty that indicates the process should be started in a detached manner. The proposal also includes movingStartSuspendedmethod to the option bag and making it a property as well (so both can be composed).namespace System.Diagnostics; { public sealed class ProcessStartOption { // Starts a new detached process with standard input, output, and error redirected to NUL. // On Windows, the process is started with DETACHED_PROCESS and CREATE_NEW_PROCESS_GROUP flags. // On macOS, the process is started with POSIX_SPAWN_SETSID. // On other Unix systems, the process calls setsid() after fork and before exec. + public bool StartDetached { get; set; } // Starts the process in a suspended state. + public bool StartSuspended { get; set; } } } namespace Microsoft.Win32.SafeHandles { public partial class SafeProcessHandle { public static SafeProcessHandle Start(ProcessStartOptions options, SafeFileHandle? input, SafeFileHandle? output, SafeFileHandle? error); - public static SafeProcessHandle StartSuspended(ProcessStartOptions options, SafeFileHandle? input, SafeFileHandle? output, SafeFileHandle? error); public void Resume(); } }Usage Example
The following example demonstrates how to start a detached process:
Alternative Designs
My main goals for
ProcessStartOptionsis to keep it simple and consistent across different operating systems.But some of the features of process creation are specific to certain platforms. For example, Windows has the
DETACHED_PROCESSflag, while Unix-like systems usesetsid(). And at the same time, I really don't want this type to become a mess likeProcessStartInfowith a lot of properties that only apply to certain platforms.So please consider following alternative designs for exposing more advanced OS-specific options for process creation, while keeping the main API simple and cross-platform.
Platform-specific derived option bags
The idea is following:
ProcessStartOptions(likeArguments,WorkingDirectory, etc.)setsid) so we can call them after fork and before exec.Sample usage:
This design is more complex and less discoverable than the previous one, but it has the advantage of keeping the main
ProcessStartOptionstype clean and focused on cross-platform features. Also, all the information can be easily obtained through the getters.List of advanced options
An abstract class, with public static factory methods for creating instances of
AdvancedProcessStartOptionswith specific features enabled. For example:Details
Usage:
The API would be more discoverable than the previous design, and it would allow for better composition of different advanced options. However, it would require creating and JITing a lot of small internal types that derive from
AdvancedProcessStartOptions. And it would just move the complexity from one place to another (with the benefit of hiding it from 95% of the users). Not to mention the lack of ability to read the advanced options back from theProcessStartOptionsinstance, which could be a problem for some scenarios.Builder for advanced options
A builder type that allows users to fluently configure advanced options for process creation.
Details
Usage:
The
ProcessStartOptionswould still be clean and focused on cross-platform features, while the advanced options would be discoverable through the builder. The builder would most likely require fewer internal types than the previous design, as it could be implemented as a single type with properties for each advanced option. But there would be no public API for reading these options, which could be a problem for some scenarios.Risks
Starting a detached process is advanced scenario, when used incorrectly, it can lead to issues such as orphaned processes that continue running indefinitely.