-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Implement WaitForExitAsync for System.Diagnostics.Process #1278
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement WaitForExitAsync for System.Diagnostics.Process #1278
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, though minor efficiency change noted. I also think the tests can be shortened a little bit.
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.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/Process.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs
Outdated
Show resolved
Hide resolved
90beef6 to
a44b7c9
Compare
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs
Outdated
Show resolved
Hide resolved
a44b7c9 to
740a615
Compare
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessTestBase.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.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/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't really doing what you want it do to, especially if you add the ThrowIfCancellationRequested I suggested earlier. I think this shouldn't use a CancellationToken at all, e.g.
Process p = CreateProcessLong();
p.Start();
Task[] tasks = (from i in Enumerable.Range(0, 10) select p.WaitForExitAsync()).ToArray();
Assert.All(tasks, t => !t.IsCompleted);
p.Kill();
Task.WaitAll(tasks);There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I apologize, but I'm not following what it's doing versus should be doing. This test is basically an aync-ified clone of SingleProcess_TryWaitMultipleTimesBeforeCompleting.
Should the two tests diverge, or is there another test that should be added in addition?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The token passed to WaitForExitAsync is already canceled prior to the call. So it's going to get only to https://github.com/dotnet/runtime/pull/1278/files#diff-06c983bcaa17665febae824710b68dbeR1397 and never get any further. If the goal is to test, for example, the event handle hookup and unhookup, this won't do that. You would instead want to create a new CancellationTokenSource, pass its token to WaitForExitAsync, then cancel the token, and then await the previously returned task.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the additional info. I updated the unit test so that both cases are covered.
I also added comments to denote the different cases:
- Token is already canceled, so return synchronous
- Task is created, then token is canceled, so exercise cleanup code
- Task is awaited, then process is killed (the happy path)
- Waiting with a token on an already exited process, so return synchronous
Please let me know if there's anything else I missed. Thanks!
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
Per review feedback, change `WaitForExitAsync` to return a `Task` instead of `Task<bool>`. Now, to determine if the process has exited, callers should check `HasExited` after the await, and cancellation follows the async pattern of setting canceled on the task.
Per code review feedback, remove the asserts that verify that the Task returned by WaitForExitAsync is completed successfully / canceled, as they're essentially Task tests and not relevant to WaitForExitAsync.
Per code review feedback, fix the MultipleProcesses_ParallelStartKillWaitAsync test to make work a `Func<Task>` instead of an `Action` since we're await-ing it.
Per code review feedback, converting ExitHandler from a local function to an `EventHandler` to avoid the extra delegate allocation to convert between the types.
Per code review, register the `TaskCompletionSource` with the `CancellationToken` so that if an `OperationCanceledException` is thrown the relevant token is available on `OperationCanceledException.CancellationToken`. To accomplish that the `TaskCompletionSourceWithCancellation` class that's internal to `System.Net.Http` is moved to Common and consumed in both places. Unit tests are also updated to verify that the cancellation token passed in is the same one returned from `OperationCanceledException.CancellationToken`.
Per code review feedback, update `WaitForExitAsync` to not require the user to call `EnableRaisingEvents` first. Setting `EnableRaisingEvents` ourselves introduces the chance for an exception in the case where the process has already exited, so I've added a comment to the top of the method to detail the possible paths through the code and added comment annotations for each case. Lastly, I updated the unit tests to remove `EnableRaisingEvents` calls.
Per code review feedback, update the new unit tests in ProcessWaitingTests to follow style guidelines.
Per code review feedback, update tests to follow coding guidelines, simplify creating cancellation tokens, and verify synchronous completion of tasks in the exited / canceled case.
Per code review feedback, add an early out for a non-exited process when canceled.
Per code review feedback, add a test for a process that completes normally and doesn't use a canellation token.
Per code review feedback, update the xmldocs for `WaitForExitAsync` to specify the language keyword for "true", and add a `<returns>` element.
76cef6d to
4555b12
Compare
|
Friendly ping to reviewers: Is there any additional follow up need here? Or is this OK to merge? |
|
This looks good to me now. @stephentoub anything else? |
|
@ericstj who is area owner here? |
|
@krwq but he's out on leave. I trust @stephentoub and @carlossanlop to review here. |
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs
Outdated
Show resolved
Hide resolved
Per code review feedback, update xmldocs to list other conditions that can cause the function to return.
Per code review feedback, update the method guards to span multiple lines to adhere to style guidelines.
Per code review feedback, update SingleProcess_TryWaitAsyncMultipleTimesBeforeCompleting to test both the case where the token is canceled before creating the Task as well as after the task is already created.
aa4235f to
54a556a
Compare
|
Friendly ping to reviewers: Is there any additional follow up need here? Or is this OK to merge? |
|
Thanks! |
This PR implements the API proposal dotnet/corefx#34689: Add a task-based
WaitForExitAsync()for theProcessclass.In order to ensure that the
Exitedevent is never lost, it is the caller's responsibility to ensure thatEnableRaisingEventsistrueprior to callingWaitForExitAsync(). A newInvalidOperationExceptionwith an appropriate error message is introduced to enforce those semantics.Additionally, per feedback,
WaitForExitAsyncreturns aTaskinstead of more closely matching the sync version and returningTask<bool>. To determine if the process has exited,callers can check
HasExitedafter the await (also cancellation follows the async pattern of throwing anOperationCanceledExceptionand setting canceled on the task).Unit tests for
WaitForExit()were duplicated to add async versions, and one sync test was missing an assert, so I added it.