runtime: API Proposal: WaitForExitAsync for System.Diagnostics.Process
Per discussion with @krwq, I’m splitting off an API proposal for a Task-based WaitForExitAsync
method on System.Diagnostics.Process
, since it is a building block API that is simpler to understand and use correctly, while the original issue (#12039) can continue to incubate additional convenience APIs.
Here’s the proposed API:
public partial class Process
{
public Task WaitForExitAsync(CancellationToken cancellationToken = default) { throw null; }
}
The method takes a single, optional parameter, a CancellationToken
, to support timeouts, which matches WaitForExit(int timeout)
semantics, while following Task-based programming best practices of allowing cancellation.
API Rationale / Design notes
API doesn’t expose an WaitForExitAsync(int timeout)
overload since CancellationToken
has a constructor that takes a timeout, and there’s CancellationToken.TimeoutAfter()
.
While the synchronous WaitForExit
returns a bool
to determine if the process exited or not, the async version returns a plain Task
. Callers can determine that the process exited in two ways:
- By checking the task’s
IsCompletedSuccessfully
- By checking the process’
HasExited
If the wait is cancelled, the caller can determine that in two ways:
- If using
await
, the task will throw aTaskCanceledException
- By checking the task’s
IsCanceled
Callers that want to emulate the old API more closely can add an extension method as follows:
public static class ProcessExtensions
{
public static Task<bool> WaitForExitWithTimeoutAsync(this Process process, int timeout)
{
using (var cts = new CancellationTokenSource(timeout))
{
try
{
await process.WaitForExitAsync(cts.Token).ConfigureAwait(false);
return process.HasExited;
}
catch (OperationCanceledException)
{
return false;
}
}
}
}
This API isn’t provided by the framework because:
- It’s targeted towards users converting code, rather than writing new idiomatic code
- It increased API surface area without clear value
- It’s easy for developers to add if needed
Because the method internally relies on the Exited
event and there’s a potential race between setting EnableRaisingEvents
and the process exiting, I introduced a new instance of InvalidOperationException
informing the caller to set EnableRaisingEvents
to make this explicit. If this is deemed undesirable coupling I can move the set into this method, at the expense of possibly throwing and catching the InvalidOperationException
from GetProcessHandle()
, which I’d rather avoid if possible.
Implementation
I have an implementation available here with tests to show sample usage: feature/34689-process-waitforexitasync.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 9
- Comments: 17 (15 by maintainers)
This was implemented in https://github.com/dotnet/runtime/pull/1278.
I’m so happy this is approved 😄
Video
Looks good as proposed.
Would it be a good idea to also add a timeout parameter? Often, that’s all you want. You don’t want to deal with creating a token just for that call.
WaitForExit
andWaitForExitAsync
could be made symmetric. Both would get timeout and token overloads. That would be the simplest API because async or not async would be orthogonal to the feature set supported.