-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Start-Process: add parameter -ExitTimeout #5471
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
Conversation
[feature] If the timeout is reached and the process hasn't finished, the process is stopped.
SteveL-MSFT
left a comment
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 contribution!
| /// </summary> | ||
| [Parameter] | ||
| [Alias("TimeoutSec")] | ||
| [ValidateNotNullOrEmpty] |
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.
Is there a specific reason for this upper limit?
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.
Usually we use [ValidateRange(0, Int32.MaxValue)] but now we have [ValidateRange(ValidateRangeKind.Positive)]
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.
@SteveL-MSFT No reason, just that the Wait-Process cmdlet has this limit so I used the same range.
@iSazonov I have changed it to [ValidateRange(ValidateRangeKind.Positive)].
| string message = StringUtil.Format(ProcessResources.StartProcessTimeoutExceeded, process.ProcessName); | ||
| ErrorRecord er = new ErrorRecord(new TimeoutException(message), "StartProcessTimeoutExceeded", ErrorCategory.OperationTimeout, process); | ||
|
|
||
| StopProcessCommand stop = new StopProcessCommand(); |
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.
Seems like Stop-Process should have a switch called -IncludeChildProcesses and you would use that here
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.
Ideally. As there currently isn't one, I implemented the GetProcessTreeIds function to do this role.
If this code is accepted, I had planned to open an issue / PR about somehow integrating this function into the Stop-Process cmdlet in the future (so as not to change too much code in this PR), instead of it being located in Start-Process.
| [Parameter] | ||
| [Alias("TimeoutSec")] | ||
| [ValidateNotNullOrEmpty] | ||
| [ValidateRange(1, 32767)] |
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 think that Timeout is a bit misleading as it's not really a timeout on starting the process, but on how long it's expected to run. Perhaps calling this -WaitForExitTimeout would be more accurate? cc @HemantMahawar @joeyaiello
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.
-ExitTimeout
Also we could type of -Wait from switch to int to accept timeout.
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.
Agreed; -ExitTimeout makes more sense. Would changing -Wait to an int cause any problems with existing scripts?
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.
We should probably leave -Wait as-is for compat reasons. -ExitTimeout works for me.
| jobObject.WaitOne(_waithandle, _timeout); | ||
| if (!process.HasExited) | ||
| { | ||
| StopProcessOnTimeout(process); |
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'm wondering if we actually want to stop the process on timeout by default. I think it might be sufficient to return a non-terminating error so that -PassThru returns the process so the user can terminate it if so desired? cc @HemantMahawar @joeyaiello
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.
From a user's perspective, I'd expect PowerShell to stop the process for me because this is how at least I would use this parameter. Even if it is just a naming problem, a -Timeout like implementation would be more useful to me than a -WaitForExitTimeout implementation.
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 wonder why we're actually making this change if the following works fine Start-Process -PassThru | Wait-Process -Timeout? It seems we duplicate a code.
I'd expect that we enhance Wait-Process cmdlet with -PassThru to get Start-Process -PassThru | Wait-Process -Timeout 10 -PassThru | Stop-Process.
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.
@SteveL-MSFT @bergmeister I thought about both of these ways when implementing this, but settled on stopping the process on timeout, similar to https://ss64.com/bash/timeout.html. The reasoning is if I started a process and gave it a time limit, I would expect it to stop if the limit is reached, rather than just let me know it had gone over the limit. Is it worth adding a switch -StopOnExitTimeout (or similar)? Or, should the below suggestion be used?
@iSazonov I see your point, and didn't think of it being run this way. I suppose adding the -ExitTimeout parameter is about simplifying the amount of cmdlets needed. Would you prefer me to implement -PassThru for Wait-Process instead?
|
|
||
| processRelationships = ps.StandardOutput.ReadToEnd(); | ||
|
|
||
| ps.WaitForExit(); |
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.
Is there a potential this could not exit?
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.
Agreed. Shall add a few seconds wait before returning, just in case.
| processRelationships = ps.StandardOutput.ReadToEnd(); | ||
|
|
||
| ps.WaitForExit(); | ||
| ps.Close(); |
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.
Should we check the exit code?
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 had assumed this would either succeed or throw an error. Shall add a check just in case.
| } | ||
|
|
||
| # Find where test/powershell is so we can find the testexe command relative to it | ||
| $powershellTestDir = $PSScriptRoot |
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.
You shouldn't have to search for testexe, I think the current pattern is to expect using Start-PSPester which builds testexe and puts it into the path
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.
Sorry, didn't realise it worked that way. Shall change to just use textexe.
iSazonov
left a comment
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 wonder why we're actually making this change if the following works fine Start-Process -PassThru | Wait-Process -Timeout? It seems we duplicate a code.
I'd expect that we enhance Wait-Process cmdlet with -PassThru to get Start-Process -PassThru | Wait-Process -Timeout 10 -PassThru | Stop-Process.
| /// </summary> | ||
| [Parameter] | ||
| [Alias("TimeoutSec")] | ||
| [ValidateNotNullOrEmpty] |
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.
Usually we use [ValidateRange(0, Int32.MaxValue)] but now we have [ValidateRange(ValidateRangeKind.Positive)]
| [Parameter] | ||
| [Alias("TimeoutSec")] | ||
| [ValidateNotNullOrEmpty] | ||
| [ValidateRange(1, 32767)] |
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.
-ExitTimeout
Also we could type of -Wait from switch to int to accept timeout.
| _timeout = value * 1000; | ||
| _timeoutSpecified = true; | ||
| } | ||
| } |
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.
We can use autogenerated property:
public int Timeout { Get; Set; } = Threading.Timeout.Infinite;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. I was not aware of doing it this way.
| [Parameter] | ||
| public SwitchParameter Wait { get; set; } | ||
|
|
||
| /// <summary> |
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.
Please follow common convention and add final dot in public comments.
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.
Added. The other public comments in Start-Process didn't have a dot, so I followed the same format as those.
| } | ||
|
|
||
| if (Wait.IsPresent) | ||
| if (Wait.IsPresent || _timeoutSpecified) |
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.
If we'll use autogenerated property we can check Timeout != Threading.Timeout.Infinite
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.
Have changed as requested
| { | ||
| if (!process.HasExited) | ||
| { | ||
| #if UNIX |
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.
We should remove two code paths and use public bool WaitForExit(int milliseconds); for all plaforms.
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'm sorry, I don't quite understand what you are asking here. Are you saying to remove the ProcessCollection / jobObject and just have a WaitForExit for all platforms? e.g.
if (!process.WaitForExit(ExitTimeout))
{
StopProcessOnTimeout(process);
}
From what I can see, the Windows specific code keeps track of any child processes that are created, which doesn't happen for UNIX. I am unsure of the reasoning behind this, as the earliest commit on GitHub for these lines does not contain any helpful information.
| jobObject.WaitOne(_waithandle, _timeout); | ||
| if (!process.HasExited) | ||
| { | ||
| StopProcessOnTimeout(process); |
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 wonder why we're actually making this change if the following works fine Start-Process -PassThru | Wait-Process -Timeout? It seems we duplicate a code.
I'd expect that we enhance Wait-Process cmdlet with -PassThru to get Start-Process -PassThru | Wait-Process -Timeout 10 -PassThru | Stop-Process.
[feature] Based on PR feedback, also: * Skipped searching for `testexe`, as `Start-PSPester` puts it into the path * Set the upper limit for `ExitTimeout` to be a positive range, rather than 32767 * Used autogenerated property for `ExitTimeout` * Set a timeout and checked the exit code for the `ps axo` process * Used common coding convention for comments
|
Hi all, Thanks for your review feedback. I have made a number of changes based on it:
Any questions that I have are in the relevant feedback comments. |
|
The concept is definitely a good one and something needed. I'd like @PowerShell/powershell-committee to review the design to make sure it fits with PowerShell norms. |
|
As I said above I'd prefer to see |
|
Is this a race? It looks like PIDs are being collected as integers (as opposed to handles that might prevent re-use), then later those PIDs are killed, but there's nothing to say that the PID hasn't been reused by the OS for a different process at that point, or is there? |
|
@stuajnht: Consistency of parameter names across cmdlets is important, so I agree with @iSazonov that we should revert to @iSazonov: Being able to specify a timeout value directly is a welcome convenience that we should add to other cmdlets as well - see #5434 and #5433 PowerShell lacks the concept of an optional argument value altogether, and I suggest not opening that can of worms with something To me, there's nothing wrong with a separate |
|
@mklement0 @iSazonov I am happy to change the parameter back to @mattpwhite After trying with a variety of programs, I believe that you are right. When I was coding this, killing the parent process would keep the child processes running. As was mentioned in one of the comments, ideally the @SteveL-MSFT Has anything been considered if the process should be stopped on the timeout being reached, or if it should just warn the user? Thanks all, and I'll update the PR once I have more information. |
To me, that calls for another switch: |
|
@stuajnht I'll try to get the committee to review this today |
|
@PowerShell/powershell-committee reviewed this and would like the parameter to be called |
|
May I ask what the rationale is for not sticking with the well-established Aside from this inconsistency, "duration" smacks of |
|
Implying |
|
@mklement0 the main push back on calling it |
|
I appreciate your letting me know. I don't think there's any point in distinguishing between launch duration and execution duration - they should be treated as a single timeout, starting at the launch point in time. If there's a technical reason for not being able to enforce the timeout during the launch phase a simple note in the documentation will do. Even if you did want to make the distinction you're suggesting, |
|
I'd prefer to see unified parameter names. These parameters (Timeout) are easier to discover and remember than the unique parameters (MaximumDuration) in each cmdlet. |
|
@mklement0 @iSazonov appreciate all the feedback and you both make valid points, will bring it back up |
|
@PowerShell/powershell-committee revisited this and would be ok with |
| [Parameter] | ||
| [ValidateNotNullOrEmpty] | ||
| [ValidateRange(ValidateRangeKind.Positive)] | ||
| public int ExitTimeout { get; set; } = Timeout.Infinite; |
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.
needs to be updated per committee review
|
This PR has been automatically marked as stale because it has not had activity in the last 30 days. It will be closed if no further activity occurs within 10 days. Thank you for your contributions. |
|
@stuajnht Could you please continue? |
|
@iSazonov Please consider the bots comment the request to continue. By commenting, you are defeating the bot. |
|
@TravisEz13 Thanks for clarify. I thought the bot only counted commits. |
|
@iSazonov But a closed PR can be continued as well as long as the owner doesn't delete the branch. The bot will label all of them as |
|
@TravisEz13 I open Issue #6446 with suggestion. |
|
This PR has been automatically marked as stale because it has not had activity in the last 30 days. It will be closed if no further activity occurs within 10 days. |
|
This PR has be automatically close because it is stale. If you wish to continue working on the PR, please first update the PR, then reopen it. |
This is to resolve #5185 by adding a
Timeoutparameter toStart-Process.If the timeout is reached and the process hasn't finished, the process is stopped.
To perform the stopping of processes, the
Stop-Processcmdlet is invoked. This is done to prevent repeating any checks needed when killing a process.Before the process is stopped, a "search" is attempted to find all descendant processes to hopefully avoid any orphaned processes remaining active. This uses WMI on Windows and the
pscommand on UNIX-like systems, as this provided the most reliable way for cross-platform support.As mentioned in the contributing guidelines, this change may warrant a note in the changelog (I am unsure where to put it).