Wait-Process: add -Any and -PassThru parameters (#19423)

This commit is contained in:
dwtaber authored and GitHub committed 2023-07-24 09:46:16 -07:00
1 parent 836c0c580b
commit 3b9e70cfc8
2 files changed
+114 -13

No files matched your search

@@ -822,6 +822,7 @@ namespace Microsoft.PowerShell.Commands
/// This class implements the Wait-process command.
/// </summary>
[Cmdlet(VerbsLifecycle.Wait, "Process", DefaultParameterSetName = "Name", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097146")]
[OutputType(typeof(Process))]
public sealed class WaitProcessCommand : ProcessBaseCommand
{
#region Parameters
@@ -895,6 +896,18 @@ namespace Microsoft.PowerShell.Commands
_timeOutSpecified = true;
}
}
/// <summary>
/// Gets or sets a value indicating whether to return after any one process exits.
/// </summary>
[Parameter]
public SwitchParameter Any { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to return the Process objects after waiting.
/// </summary>
[Parameter]
public SwitchParameter PassThru { get; set; }
private int _timeout = 0;
private bool _timeOutSpecified;
@@ -927,7 +940,7 @@ namespace Microsoft.PowerShell.Commands
// Handle Exited event and display process information.
private void myProcess_Exited(object sender, System.EventArgs e)
{
if (System.Threading.Interlocked.Decrement(ref _numberOfProcessesToWaitFor) == 0)
if (Any || (Interlocked.Decrement(ref _numberOfProcessesToWaitFor) == 0))
{
_waitHandle?.Set();
}
@@ -979,7 +992,12 @@ namespace Microsoft.PowerShell.Commands
{
try
{
if (!process.HasExited)
// Check for processes that exit too soon for us to add an event.
if (Any && process.HasExited)
{
_waitHandle.Set();
}
else if (!process.HasExited)
{
process.EnableRaisingEvents = true;
process.Exited += myProcess_Exited;
@@ -995,11 +1013,12 @@ namespace Microsoft.PowerShell.Commands
}
}
bool hasTimedOut = false;
if (_numberOfProcessesToWaitFor > 0)
{
if (_timeOutSpecified)
{
_waitHandle.WaitOne(_timeout * 1000);
hasTimedOut = !_waitHandle.WaitOne(_timeout * 1000);
}
else
{
@@ -1007,21 +1026,29 @@ namespace Microsoft.PowerShell.Commands
}
}
foreach (Process process in _processList)
if (hasTimedOut || (!Any && _numberOfProcessesToWaitFor > 0))
{
try
foreach (Process process in _processList)
{
if (!process.HasExited)
try
{
string message = StringUtil.Format(ProcessResources.ProcessNotTerminated, new object[] { process.ProcessName, process.Id });
ErrorRecord errorRecord = new(new TimeoutException(message), "ProcessNotTerminated", ErrorCategory.CloseError, process);
WriteError(errorRecord);
if (!process.HasExited)
{
string message = StringUtil.Format(ProcessResources.ProcessNotTerminated, new object[] { process.ProcessName, process.Id });
ErrorRecord errorRecord = new(new TimeoutException(message), "ProcessNotTerminated", ErrorCategory.CloseError, process);
WriteError(errorRecord);
}
}
catch (Win32Exception exception)
{
WriteNonTerminatingError(process, exception, ProcessResources.ProcessIsNotTerminated, "ProcessNotTerminated", ErrorCategory.CloseError);
}
}
catch (Win32Exception exception)
{
WriteNonTerminatingError(process, exception, ProcessResources.ProcessIsNotTerminated, "ProcessNotTerminated", ErrorCategory.CloseError);
}
}
if (PassThru)
{
WriteObject(_processList, enumerateCollection: true);
}
}
@@ -0,0 +1,74 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
Describe "Wait-Process" {
BeforeAll {
$pingCommandPath = (Get-Command -CommandType Application ping)[0].Definition
$startProcessArgs = @{
FilePath = $pingCommandPath
PassThru = $true
}
$nc = $IsWindows ? "-n" : "-c"
function longPing { Start-Process @startProcessArgs -ArgumentList "$nc 10 localhost" }
function shortPing { Start-Process @startProcessArgs -ArgumentList "$nc 2 localhost" }
}
BeforeEach {
$Processes = @( 1..3 | ForEach-Object { longPing } ) + ($shortPing = shortPing)
}
AfterEach {
Stop-Process -InputObject $Processes
}
It "Should wait until all processes have exited" {
Wait-Process -InputObject $Processes
$Processes.Where({$_.HasExited -eq $true}).Count | Should -Be $Processes.Count
}
It "Should return after all processes have exited, even if some exit before the wait starts." {
Wait-UntilTrue -sb { $shortPing.HasExited -eq $true } -IntervalInMilliseconds 100
Wait-Process -InputObject $Processes
$Processes.Where({$_.HasExited -eq $true}).Count | Should -Be $Processes.Count
}
It "Should return immediately if all processes have exited before the wait starts" {
Wait-UntilTrue -sb { $Processes.HasExited -NotContains $false } -IntervalInMilliseconds 100
Wait-Process -InputObject $Processes
$Processes.Where({$_.HasExited -eq $true}).Count | Should -Be $Processes.Count
}
It "Should return immediately if at least one process has exited before the wait starts" {
Wait-UntilTrue -sb { $shortPing.HasExited -eq $true } -IntervalInMilliseconds 100
Wait-Process -InputObject $Processes -Any
$Processes.Where({$_.HasExited -eq $true}).Count | Should -Be 1
$Processes.Where({$_.HasExited -eq $false}).Count | Should -Be ($Processes.Count - 1)
}
It "Should wait until any one process has exited" {
Wait-Process -InputObject $Processes -Any
$Processes.Where({$_.HasExited -eq $true}).Count | Should -Be 1
$Processes.Where({$_.HasExited -eq $false}).Count | Should -Be ($Processes.Count - 1)
}
It "Should passthru all processes when all processes have exited" {
$PassThruProcesses = Wait-Process -InputObject $Processes -PassThru
$PassThruProcesses.Where({$_.HasExited -eq $true}).Count | Should -Be $Processes.Count
}
It "Should passthru all processes when any one process has exited" {
$PassThruProcesses = Wait-Process -InputObject $Processes -Any -PassThru
$PassThruProcesses.Where({$_.HasExited -eq $true}).Count | Should -Be 1
$PassThruProcesses.Where({$_.HasExited -eq $false}).Count | Should -Be ($Processes.Count - 1)
}
}