Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
321e5cc
Wait-Process: add `-Any` parameter
dwtaber Mar 29, 2023
f48f1f6
Wait-Process: add `-PassThru` parameter
dwtaber Mar 29, 2023
a3c2371
Wait-Process: use `Any` instead of `_any` in ifs
dwtaber Mar 29, 2023
b01e771
Remove inadvertent extra whitespace
dwtaber Mar 29, 2023
5072e0c
Merge branch 'PowerShell:master' into wait-process-any
dwtaber Mar 29, 2023
0fb665d
Rewrite new parameters as auto-properties
dwtaber Mar 30, 2023
455436a
Simplify conditional on myProcess_Exited
dwtaber Mar 30, 2023
8a059c0
Update new parameters' summaries per CodeFactor
dwtaber Mar 30, 2023
1331415
Reorder conditions in myProcess_Exited
dwtaber Mar 30, 2023
6060a92
remove _initialNumberOfProcesses, add hasTimedOut
dwtaber Apr 1, 2023
1a33f66
Update src/Microsoft.PowerShell.Commands.Management/commands/manageme…
dwtaber Apr 2, 2023
3c74053
Merge branch 'PowerShell:master' into wait-process-any
dwtaber Jun 8, 2023
7147bf8
Add tests for Wait-Process
dwtaber Jun 10, 2023
a6f66b7
Process.cs: add OutputType; named argument
dwtaber Jun 10, 2023
47b3709
Reduced whitespace before Should in tests
dwtaber Jun 10, 2023
21cc089
Update test/powershell/Modules/Microsoft.PowerShell.Management/Wait-P…
dwtaber Jun 19, 2023
9ba8e30
Add fixes/tests for processes that end before wait
dwtaber Jun 22, 2023
3709806
Merge branch 'PowerShell:master' into wait-process-any
dwtaber Jun 23, 2023
97ee03f
Remove blank line before `else if`
dwtaber Jun 23, 2023
69219f1
Remove WindowStyle details from tests.
dwtaber Jul 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,7 @@ private static string RetrieveProcessUserName(Process process)
/// 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
Expand Down Expand Up @@ -895,6 +896,18 @@ public int Timeout
_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;
Expand Down Expand Up @@ -927,7 +940,7 @@ public void Dispose()
// 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();
}
Expand Down Expand Up @@ -979,7 +992,12 @@ protected override void EndProcessing()
{
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;
Expand All @@ -995,33 +1013,42 @@ protected override void EndProcessing()
}
}

bool hasTimedOut = false;
if (_numberOfProcessesToWaitFor > 0)
{
if (_timeOutSpecified)
{
_waitHandle.WaitOne(_timeout * 1000);
hasTimedOut = !_waitHandle.WaitOne(_timeout * 1000);
}
else
{
_waitHandle.WaitOne();
}
}

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);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}