From 266b6e7de0fecf00ed1cfb73ea1a5c37ea674ad4 Mon Sep 17 00:00:00 2001 From: Max Kozlov Date: Thu, 13 Apr 2017 16:21:39 +0300 Subject: [PATCH 01/17] Rewritten Wait-RSJob for #139, #140, Additional tests for Wait-RSJob, OnRemove tests fixed --- PoshRSJob/Public/Wait-RSJob.ps1 | 194 +++++++++++++++++--------------- Tests/PoshRSJob.Tests.ps1 | 70 +++++++++--- 2 files changed, 157 insertions(+), 107 deletions(-) diff --git a/PoshRSJob/Public/Wait-RSJob.ps1 b/PoshRSJob/Public/Wait-RSJob.ps1 index b5e04ac..9426c67 100644 --- a/PoshRSJob/Public/Wait-RSJob.ps1 +++ b/PoshRSJob/Public/Wait-RSJob.ps1 @@ -32,8 +32,8 @@ Function Wait-RSJob { Waits for jobs that have data being outputted. You can specify -HasMoreData:$False to wait for jobs that have no data to output. - .PARAMETER Timeout - Timeout after specified number of seconds. This is a global timeout meaning that it is not a per + .PARAMETER Timeout + Timeout after specified number of seconds. This is a global timeout meaning that it is not a per job timeout. .PARAMETER ShowProgress @@ -42,7 +42,7 @@ Function Wait-RSJob { .NOTES Name: Wait-RSJob Author: Ryan Bushe/Boe Prox - Notes: This function is a slightly modified version of Get-RSJob by Boe Prox.(~10 lines of code changed) + Notes: This function is a slightly modified version of Get-RSJob by Boe Prox.(~10 lines of code changed) .EXAMPLE Get-RSJob | Wait-RSJob @@ -63,134 +63,148 @@ Function Wait-RSJob { ParameterSetName='Id')] [int[]]$Id, [parameter(ValueFromPipelineByPropertyName=$True, - ParameterSetName='Guid')] - [guid[]]$InstanceID, + ParameterSetName='InstanceID')] + [string[]]$InstanceID, [parameter(ValueFromPipelineByPropertyName=$True, ParameterSetName='Batch')] [string[]]$Batch, [parameter(ParameterSetName='Batch')] [parameter(ParameterSetName='Name')] [parameter(ParameterSetName='Id')] - [parameter(ParameterSetName='Guid')] + [parameter(ParameterSetName='InstanceID')] [parameter(ParameterSetName='All')] [ValidateSet('NotStarted','Running','Completed','Failed','Stopping','Stopped','Disconnected')] [string[]]$State, [parameter(ParameterSetName='Batch')] [parameter(ParameterSetName='Name')] [parameter(ParameterSetName='Id')] - [parameter(ParameterSetName='Guid')] + [parameter(ParameterSetName='InstanceID')] [parameter(ParameterSetName='All')] [Switch]$HasMoreData, - [int]$Timeout, + [int]$Timeout, [switch]$ShowProgress ) Begin { If ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' } - Write-Verbose "ParameterSet: $($PSCmdlet.parametersetname)" - $List = New-Object System.Collections.ArrayList - $WhereList = New-Object System.Collections.ArrayList - + Write-Verbose "ParameterSet: $($PSCmdlet.ParameterSetName)" + $WaitJobs = New-Object System.Collections.ArrayList + $Hash = @{} + $Property = $PSCmdlet.ParameterSetName + $IsPipeline = $true #Take care of bound parameters If ($PSBoundParameters['Name']) { - [void]$list.AddRange($Name) + $IsPipeline = $false + foreach ($v in $Name) { + $Hash.Add($v,1) + } } If ($PSBoundParameters['Batch']) { - [void]$list.AddRange($Batch) + $IsPipeline = $false + foreach ($v in $Batch) { + $Hash.Add($v,1) + } } If ($PSBoundParameters['Id']) { - [void]$list.AddRange($Id) + $IsPipeline = $false + foreach ($v in $Id) { + $Hash.Add($v,1) + } } If ($PSBoundParameters['InstanceId']) { - [void]$list.AddRange($InstanceId) + $IsPipeline = $false + foreach ($v in $InstanceId) { + $Hash.Add($v,1) + } } - If ($PSBoundParameters['Job']){ - [void]$list.AddRange($Job) - } - } - Process { - If ($PSCmdlet.ParameterSetName -ne 'All') { - Write-Verbose "Adding $($_)" - [void]$List.Add($_) + If ($PSBoundParameters['Job']){ + $IsPipeline = $false + [void]$WaitJobs.AddRange($Job) } } - End { - If ($List.count -eq 0) { - return - } - Switch ($PSCmdlet.parametersetname) { - 'Name' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_}) -join '|') -replace '\*','.*' - [void]$WhereList.Add("`$_.Name -match $Items") - } - 'Id' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_}) -join '|') - [void]$WhereList.Add("`$_.Id -match $Items") + Process { + Write-Debug "ParameterSet: $($PSCmdlet.ParameterSetName)" + if ($IsPipeline) { + $Property = $PSCmdlet.ParameterSetName + if ($PSCmdlet.ParameterSetName -eq 'Job') { + [void]$WaitJobs.AddRange($Job) } - 'Guid' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_}) -join '|') - [void]$WhereList.Add("`$_.InstanceId -match $Items") + elseif ($PSCmdlet.ParameterSetName -ne 'All') { + Write-Verbose "Adding $($PSBoundParameters[$Property])" + foreach ($v in $PSBoundParameters[$Property]) { + $Hash.Add($v,1) + } } - 'Job' { - $Items = '"{0}"' -f (($list | Select-Object -ExpandProperty Id | ForEach-Object {"^{0}$" -f $_}) -join '|') - [void]$WhereList.Add("`$_.id -match $Items") - } - 'Batch' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_}) -join '|') - [void]$WhereList.Add("`$_.batch -match $Items") - } } - If ($PSBoundParameters['State']) { + } + End { + $WhereList = New-Object System.Collections.ArrayList + if ($PSBoundParameters['State']) { [void]$WhereList.Add("`$_.State -match `"$($State -join '|')`"") } - If ($PSBoundParameters.ContainsKey('HasMoreData')) { + if ($PSBoundParameters.ContainsKey('HasMoreData')) { [void]$WhereList.Add("`$_.HasMoreData -eq `$$HasMoreData") } - If ($WhereList.count -gt 0) { - $WhereString = $WhereList -join ' -AND ' - $ScriptBlock = [scriptblock]::Create($WhereString) - } - If ($ScriptBlock) { - Write-Verbose "WhereString: $($WhereString)" - Write-Verbose "Using scriptblock" - $FilteredJobs = @($PoshRS_Jobs | Where-Object $ScriptBlock) - $WaitJobs = $FilteredJobs - $TotalJobs = $FilteredJobs.Count - Write-Verbose "$($FilteredJobs.Count)" - $Date = Get-Date - $Completed = 0 - Do{ - #only ever check $WaitJobs State once per loop, and do all operations based on that snapshot to avoid bugs where the state of a job may have changed mid loop - $JustFinishedJobs = New-Object System.Collections.ArrayList - $RunningJobs = New-Object System.Collections.ArrayList - ForEach ($WaitJob in $WaitJobs) { - If($WaitJob.State -match 'Completed|Failed|Stopped|Suspended|Disconnected' -and $WaitJob.Completed) { - [void]$JustFinishedJobs.Add($WaitJob) - } Else { - [void]$RunningJobs.Add($WaitJob) - } + # IF faster than any scriptblocks + if ($PSCmdlet.ParameterSetName -ne 'Job') { + if ($PSCmdlet.ParameterSetName -eq 'All') { + $WaitJobs = $PoshRS_Jobs + } + else { + foreach ($job in $PoshRS_Jobs) { + if ($Hash.ContainsKey($job.$Property)) { + [void]$WaitJobs.Add($job) + } + } + } + } + if ($WaitJobs.Count -and $PSBoundParameters.ContainsKey('State')) { + $States = '^' + $State -join '$|^' + '$' + $WaitJobs = foreach ($job in $WaitJobs) { + if ($job.State -match $States) { + $job + } + } + } + if ($WaitJobs.Count -and $PSBoundParameters.ContainsKey('HasMoreData')) { + $WaitJobs = foreach ($job in $WaitJobs) { + if ($job.HasMoreData -eq $HasMoreData) { + $job + } + } + } + $TotalJobs = $WaitJobs.Count + $Completed = 0 + Write-Verbose "Wait for $($TotalJobs) jobs" + $Date = Get-Date + while ($Waitjobs.Count -ne 0) { + Start-Sleep -Milliseconds 100 + #only ever check $WaitJobs State once per loop, and do all operations based on that snapshot to avoid bugs where the state of a job may have changed mid loop + $JustFinishedJobs = New-Object System.Collections.ArrayList + $RunningJobs = New-Object System.Collections.ArrayList + ForEach ($WaitJob in $WaitJobs) { + If($WaitJob.State -match 'Completed|Failed|Stopped|Suspended|Disconnected' -and $WaitJob.Completed) { + [void]$JustFinishedJobs.Add($WaitJob) + } Else { + [void]$RunningJobs.Add($WaitJob) } - $WaitJobs = $RunningJobs + } + $WaitJobs = $RunningJobs - $JustFinishedJobs + $JustFinishedJobs - $Completed += $JustFinishedJobs.Count - Write-Verbose "Wait: $($Waitjobs.Count)" - Write-Verbose "Completed: ($Completed)" - Write-Verbose "Total: ($Totaljobs)" - Write-Verbose "Status: $($Completed/$TotalJobs)" - If ($PSBoundParameters.ContainsKey('ShowProgress')) { - Write-Progress -Activity "RSJobs Tracker" -Status ("Remaining Jobs: {0}" -f $Waitjobs.Count) -PercentComplete (($Completed/$TotalJobs)*100) - } - if($Timeout){ - if((New-Timespan $Date).TotalSeconds -ge $Timeout){ - $TimedOut = $True - break - } - } - } - While($Waitjobs.Count -ne 0) + $Completed += $JustFinishedJobs.Count + Write-Debug "Wait: $($Waitjobs.Count)" + Write-Debug "Completed: ($Completed)" + Write-Debug "Total: ($Totaljobs)" + Write-Debug "Status: $($Completed/$TotalJobs)" + If ($PSBoundParameters.ContainsKey('ShowProgress')) { + Write-Progress -Activity "RSJobs Tracker" -Status ("Remaining Jobs: {0}" -f $Waitjobs.Count) -PercentComplete (($Completed/$TotalJobs)*100) + } + if($Timeout -and (New-Timespan $Date).TotalSeconds -ge $Timeout){ + break + } } } } diff --git a/Tests/PoshRSJob.Tests.ps1 b/Tests/PoshRSJob.Tests.ps1 index 48e6ffb..2c1ea8f 100644 --- a/Tests/PoshRSJob.Tests.ps1 +++ b/Tests/PoshRSJob.Tests.ps1 @@ -237,23 +237,59 @@ Describe "Wait-RSJob PS$PSVersion" { $EndDate = Get-Date ( $EndDate - $StartDate ).TotalSeconds -gt 5 | Should be $True } + It 'should wait for jobs by Name' { + $TestJob = 1..3 | Foreach { + Start-RSJob -Name "NameTest1$_" @Verbose -ScriptBlock { + Start-Sleep -seconds 5 + Get-Date + } } + [array]$Result = Wait-RSJob -Name ($TestJob | Select -Expand Name) # Omitted verbose to avoid clutter + $Result.Count -eq $TestJob.Count | Should be $True + } + It 'should wait for jobs by Name from pipeline' { + $TestJob = 1..3 | Foreach { + Start-RSJob -Name "NameTest2$_" @Verbose -ScriptBlock { + Start-Sleep -seconds 5 + Get-Date + } } + [array]$Result = $TestJob | Select Name | Wait-RSJob # Omitted verbose to avoid clutter + $Result.Count -eq $TestJob.Count | Should be $True + } + It 'should wait for jobs by id' { + $TestJob = 1..3 | Foreach { + Start-RSJob @Verbose -ScriptBlock { + Start-Sleep -seconds 5 + Get-Date + } } + [array]$Result = Wait-RSJob -Id ($TestJob | Select -Expand Id) # Omitted verbose to avoid clutter + $Result.Count -eq $TestJob.Count | Should be $True + } + It 'should wait for jobs by InstanceId' { + $TestJob = 1..3 | Foreach { + Start-RSJob @Verbose -ScriptBlock { + Start-Sleep -seconds 5 + Get-Date + } } + [array]$Result = Wait-RSJob -InstanceId ($TestJob | Select -Expand InstanceId) # Omitted verbose to avoid clutter + $Result.Count -eq $TestJob.Count | Should be $True + } } } Describe "Test RSJob Throttling" { - It "Full Pipe input" { - $StartDate = Get-Date - Test-RSJob $true - $EndDate = Get-Date - ( $EndDate - $StartDate ).TotalSeconds -gt 25 | Should be $True - } - It "OneByOne Pipe input" { - $StartDate = Get-Date - Test-RSJob $false - $EndDate = Get-Date - ( $EndDate - $StartDate ).TotalSeconds -gt 25 | Should be $True - } + It "Full Pipe input" { + $StartDate = Get-Date + Test-RSJob $true + $EndDate = Get-Date + ( $EndDate - $StartDate ).TotalSeconds -gt 25 | Should be $True + } + It "OneByOne Pipe input" { + $StartDate = Get-Date + Test-RSJob $false + $EndDate = Get-Date + ( $EndDate - $StartDate ).TotalSeconds -gt 25 | Should be $True + } } @@ -262,11 +298,11 @@ Describe "Module OnRemove Actions PS$PSVersion" { Get-RSJob | Remove-RSJob Remove-Module -Name PoshRSJob -ErrorAction SilentlyContinue It 'should remove all variables' { - {Get-Variable Jobs -ErrorAction Stop} | Should Throw - {Get-Variable JobCleanup -ErrorAction Stop} | Should Throw - {Get-Variable JobID -ErrorAction Stop} | Should Throw - {Get-Variable RunspacePoolCleanup -ErrorAction Stop} | Should Throw - {Get-Variable RunspacePools -ErrorAction Stop} | Should Throw + {Get-Variable PoshRS_Jobs -ErrorAction Stop} | Should Throw + {Get-Variable PoshRS_JobCleanup -ErrorAction Stop} | Should Throw + {Get-Variable PoshRS_JobID -ErrorAction Stop} | Should Throw + {Get-Variable PoshRS_RunspacePoolCleanup -ErrorAction Stop} | Should Throw + {Get-Variable PoshRS_RunspacePools -ErrorAction Stop} | Should Throw } } } From 44bdc9909c9521f829b6bb074df6e876d9c6a106 Mon Sep 17 00:00:00 2001 From: Max Kozlov Date: Thu, 13 Apr 2017 16:27:20 +0300 Subject: [PATCH 02/17] Code formatting --- PoshRSJob/Public/Wait-RSJob.ps1 | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/PoshRSJob/Public/Wait-RSJob.ps1 b/PoshRSJob/Public/Wait-RSJob.ps1 index 9426c67..92f1bd7 100644 --- a/PoshRSJob/Public/Wait-RSJob.ps1 +++ b/PoshRSJob/Public/Wait-RSJob.ps1 @@ -126,7 +126,7 @@ Function Wait-RSJob { Process { Write-Debug "ParameterSet: $($PSCmdlet.ParameterSetName)" if ($IsPipeline) { - $Property = $PSCmdlet.ParameterSetName + $Property = $PSCmdlet.ParameterSetName if ($PSCmdlet.ParameterSetName -eq 'Job') { [void]$WaitJobs.AddRange($Job) } @@ -146,21 +146,21 @@ Function Wait-RSJob { if ($PSBoundParameters.ContainsKey('HasMoreData')) { [void]$WhereList.Add("`$_.HasMoreData -eq `$$HasMoreData") } - # IF faster than any scriptblocks + # IF faster than any scriptblocks if ($PSCmdlet.ParameterSetName -ne 'Job') { - if ($PSCmdlet.ParameterSetName -eq 'All') { - $WaitJobs = $PoshRS_Jobs - } - else { - foreach ($job in $PoshRS_Jobs) { - if ($Hash.ContainsKey($job.$Property)) { - [void]$WaitJobs.Add($job) - } - } + if ($PSCmdlet.ParameterSetName -eq 'All') { + $WaitJobs = $PoshRS_Jobs + } + else { + foreach ($job in $PoshRS_Jobs) { + if ($Hash.ContainsKey($job.$Property)) { + [void]$WaitJobs.Add($job) + } + } } } if ($WaitJobs.Count -and $PSBoundParameters.ContainsKey('State')) { - $States = '^' + $State -join '$|^' + '$' + $States = '^' + $State -join '$|^' + '$' $WaitJobs = foreach ($job in $WaitJobs) { if ($job.State -match $States) { $job From 28eba96157673dbbe4c2c40624804412ef9ca922 Mon Sep 17 00:00:00 2001 From: Max Kozlov Date: Thu, 13 Apr 2017 16:47:42 +0300 Subject: [PATCH 03/17] Artefacts cleanup, strong typing --- PoshRSJob/Public/Wait-RSJob.ps1 | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/PoshRSJob/Public/Wait-RSJob.ps1 b/PoshRSJob/Public/Wait-RSJob.ps1 index 92f1bd7..0690388 100644 --- a/PoshRSJob/Public/Wait-RSJob.ps1 +++ b/PoshRSJob/Public/Wait-RSJob.ps1 @@ -41,8 +41,7 @@ Function Wait-RSJob { .NOTES Name: Wait-RSJob - Author: Ryan Bushe/Boe Prox - Notes: This function is a slightly modified version of Get-RSJob by Boe Prox.(~10 lines of code changed) + Author: Ryan Bushe/Boe Prox/Max Kozlov .EXAMPLE Get-RSJob | Wait-RSJob @@ -139,13 +138,6 @@ Function Wait-RSJob { } } End { - $WhereList = New-Object System.Collections.ArrayList - if ($PSBoundParameters['State']) { - [void]$WhereList.Add("`$_.State -match `"$($State -join '|')`"") - } - if ($PSBoundParameters.ContainsKey('HasMoreData')) { - [void]$WhereList.Add("`$_.HasMoreData -eq `$$HasMoreData") - } # IF faster than any scriptblocks if ($PSCmdlet.ParameterSetName -ne 'Job') { if ($PSCmdlet.ParameterSetName -eq 'All') { @@ -161,14 +153,14 @@ Function Wait-RSJob { } if ($WaitJobs.Count -and $PSBoundParameters.ContainsKey('State')) { $States = '^' + $State -join '$|^' + '$' - $WaitJobs = foreach ($job in $WaitJobs) { + [array]$WaitJobs = foreach ($job in $WaitJobs) { if ($job.State -match $States) { $job } } } if ($WaitJobs.Count -and $PSBoundParameters.ContainsKey('HasMoreData')) { - $WaitJobs = foreach ($job in $WaitJobs) { + [array]$WaitJobs = foreach ($job in $WaitJobs) { if ($job.HasMoreData -eq $HasMoreData) { $job } From 853fbfb8075e45bce4fbfe8d69273b83a78053bf Mon Sep 17 00:00:00 2001 From: Max Kozlov Date: Thu, 13 Apr 2017 19:00:09 +0300 Subject: [PATCH 04/17] Wait-RSJob without parameters should not return any data --- PoshRSJob/Public/Wait-RSJob.ps1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/PoshRSJob/Public/Wait-RSJob.ps1 b/PoshRSJob/Public/Wait-RSJob.ps1 index 0690388..7cc510a 100644 --- a/PoshRSJob/Public/Wait-RSJob.ps1 +++ b/PoshRSJob/Public/Wait-RSJob.ps1 @@ -141,7 +141,10 @@ Function Wait-RSJob { # IF faster than any scriptblocks if ($PSCmdlet.ParameterSetName -ne 'Job') { if ($PSCmdlet.ParameterSetName -eq 'All') { - $WaitJobs = $PoshRS_Jobs + if ($PSBoundParameters.ContainsKey('State') -or + $PSBoundParameters.ContainsKey('HasMoreData')) { + $WaitJobs = $PoshRS_Jobs + } } else { foreach ($job in $PoshRS_Jobs) { From d99c16c388a126d1162dfa85e5ff28cbd65c0272 Mon Sep 17 00:00:00 2001 From: Max Kozlov Date: Fri, 14 Apr 2017 23:00:50 +0300 Subject: [PATCH 05/17] And where did I look before? Great simplification --- PoshRSJob/Public/Wait-RSJob.ps1 | 50 ++++++--------------------------- 1 file changed, 8 insertions(+), 42 deletions(-) diff --git a/PoshRSJob/Public/Wait-RSJob.ps1 b/PoshRSJob/Public/Wait-RSJob.ps1 index 7cc510a..65d3e0b 100644 --- a/PoshRSJob/Public/Wait-RSJob.ps1 +++ b/PoshRSJob/Public/Wait-RSJob.ps1 @@ -87,53 +87,19 @@ Function Wait-RSJob { If ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' } - Write-Verbose "ParameterSet: $($PSCmdlet.ParameterSetName)" $WaitJobs = New-Object System.Collections.ArrayList $Hash = @{} - $Property = $PSCmdlet.ParameterSetName - $IsPipeline = $true - #Take care of bound parameters - If ($PSBoundParameters['Name']) { - $IsPipeline = $false - foreach ($v in $Name) { - $Hash.Add($v,1) - } - } - If ($PSBoundParameters['Batch']) { - $IsPipeline = $false - foreach ($v in $Batch) { - $Hash.Add($v,1) - } - } - If ($PSBoundParameters['Id']) { - $IsPipeline = $false - foreach ($v in $Id) { - $Hash.Add($v,1) - } - } - If ($PSBoundParameters['InstanceId']) { - $IsPipeline = $false - foreach ($v in $InstanceId) { - $Hash.Add($v,1) - } - } - If ($PSBoundParameters['Job']){ - $IsPipeline = $false - [void]$WaitJobs.AddRange($Job) - } } Process { Write-Debug "ParameterSet: $($PSCmdlet.ParameterSetName)" - if ($IsPipeline) { - $Property = $PSCmdlet.ParameterSetName - if ($PSCmdlet.ParameterSetName -eq 'Job') { - [void]$WaitJobs.AddRange($Job) - } - elseif ($PSCmdlet.ParameterSetName -ne 'All') { - Write-Verbose "Adding $($PSBoundParameters[$Property])" - foreach ($v in $PSBoundParameters[$Property]) { - $Hash.Add($v,1) - } + $Property = $PSCmdlet.ParameterSetName + if ($PSCmdlet.ParameterSetName -eq 'Job') { + [void]$WaitJobs.AddRange($Job) + } + elseif ($PSCmdlet.ParameterSetName -ne 'All') { + Write-Verbose "Adding $($PSBoundParameters[$Property])" + foreach ($v in $PSBoundParameters[$Property]) { + $Hash.Add($v,1) } } } From 9e52c5b2556f47d358464075afa9fa0338e19101 Mon Sep 17 00:00:00 2001 From: Max Kozlov Date: Wed, 19 Apr 2017 14:20:32 +0300 Subject: [PATCH 06/17] Unified Get-RSJob, Receive-RSJob, Remove-RSJob, Stop-RSJob, Wait-RSJob interface. Extra trailing spaces cleanup --- PoshRSJob/Public/Get-RSJob.ps1 | 148 +++++++++++++---------------- PoshRSJob/Public/Receive-RSJob.ps1 | 128 ++++++------------------- PoshRSJob/Public/Remove-RSJob.ps1 | 124 ++++++++---------------- PoshRSJob/Public/Stop-RSJob.ps1 | 110 ++++++--------------- PoshRSJob/Public/Wait-RSJob.ps1 | 75 +++++---------- Tests/PoshRSJob.Tests.ps1 | 48 +++++----- 6 files changed, 212 insertions(+), 421 deletions(-) diff --git a/PoshRSJob/Public/Get-RSJob.ps1 b/PoshRSJob/Public/Get-RSJob.ps1 index 4bca32a..79ca9fc 100644 --- a/PoshRSJob/Public/Get-RSJob.ps1 +++ b/PoshRSJob/Public/Get-RSJob.ps1 @@ -7,14 +7,20 @@ Function Get-RSJob { Get-RSJob will display all jobs that are currently available to include completed and currently running jobs. If no parameters are given, all jobs are displayed to view. + .PARAMETER Job + Represents the RSJob object being sent to query for. + .PARAMETER Name The name of the jobs to query for. .PARAMETER ID - The ID of the jobs that you want to display. + The ID of the jobs to query for. .PARAMETER InstanceID - The GUID of the jobs that you want to display. + The GUID of the jobs to query for. + + .PARAMETER Batch + Name of the set of jobs to query for. .PARAMETER State The State of the job that you want to display. Accepted values are: @@ -27,16 +33,13 @@ Function Get-RSJob { Stopped Disconnected - .PARAMETER Batch - Name of the set of jobs - .PARAMETER HasMoreData Displays jobs that have data being outputted. You can specify -HasMoreData:$False to display jobs - that have no data to output. + that have no data to output. .NOTES Name: Get-RSJob - Author: Boe Prox + Author: Boe Prox/Max Kozlov .EXAMPLE Get-RSJob -State Completed @@ -64,108 +67,89 @@ Function Get-RSJob { )] Param ( [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, + ParameterSetName='Job', Position=0)] + [Alias('InputObject')] + [RSJob[]]$Job, + [parameter(ValueFromPipelineByPropertyName=$True, ParameterSetName='Name')] [string[]]$Name, [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, ParameterSetName='Id')] [int[]]$Id, - [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, - ParameterSetName='Guid')] - [guid[]]$InstanceID, + [parameter(ValueFromPipelineByPropertyName=$True, + ParameterSetName='InstanceID')] + [string[]]$InstanceID, + [parameter(ValueFromPipelineByPropertyName=$True, + ParameterSetName='Batch')] + [string[]]$Batch, + [parameter(ParameterSetName='Batch')] [parameter(ParameterSetName='Name')] [parameter(ParameterSetName='Id')] - [parameter(ParameterSetName='Guid')] + [parameter(ParameterSetName='InstanceID')] [parameter(ParameterSetName='All')] [ValidateSet('NotStarted','Running','Completed','Failed','Stopping','Stopped','Disconnected')] [string[]]$State, - [parameter(ValueFromPipelineByPropertyName=$True, - ParameterSetName='Batch')] - [string[]]$Batch, [parameter(ParameterSetName='Batch')] [parameter(ParameterSetName='Name')] [parameter(ParameterSetName='Id')] - [parameter(ParameterSetName='Guid')] + [parameter(ParameterSetName='InstanceID')] [parameter(ParameterSetName='All')] - [Switch]$HasMoreData, - [parameter(ValueFromPipeline=$True,ParameterSetName='Job')] - [RSJob[]]$Job + [Switch]$HasMoreData ) Begin { If ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' - } - Write-Debug "ParameterSet: $($PSCmdlet.parametersetname)" - $List = New-Object System.Collections.ArrayList - $WhereList = New-Object System.Collections.ArrayList - - #Take care of bound parameters - $Bound = $False - If ($PSBoundParameters['Name']) { - [void]$list.AddRange($Name) - $Bound = $True } - If ($PSBoundParameters['Batch']) { - [void]$list.AddRange($Batch) - $Bound = $True - } - If ($PSBoundParameters['Id']) { - [void]$list.AddRange($Id) - $Bound = $True - } - If ($PSBoundParameters['InstanceId']) { - [void]$list.AddRange($InstanceId) - $Bound = $True - } - If ($PSBoundParameters['Job']){ - [void]$list.AddRange($Job) - $Bound = $True - } + Write-Debug "ParameterSet: $($PSCmdlet.parametersetname)" + $Hash = @{} } Process { - If ($PSCmdlet.ParameterSetName -ne 'All' -AND -NOT $Bound) { - Write-Verbose "Adding $($_)" - [void]$List.Add($_) - } - } - End { - Switch ($PSCmdlet.parametersetname) { - 'Name' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_}) -join '|') -replace '\*','.*' - [void]$WhereList.Add("`$_.Name -match $Items") - } - 'Id' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_}) -join '|') - [void]$WhereList.Add("`$_.Id -match $Items") + Write-Debug "ParameterSet: $($PSCmdlet.ParameterSetName)" + $Property = $PSCmdlet.ParameterSetName + + if ($PSCmdlet.ParameterSetName -eq 'Job') { + Write-Verbose "Adding Job $($PSBoundParameters[$Property])" + foreach ($v in $PSBoundParameters[$Property]) { + $Hash.Add($v.ID,1) } - 'Guid' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_}) -join '|') - [void]$WhereList.Add("`$_.InstanceId -match $Items") + } + elseif ($PSCmdlet.ParameterSetName -ne 'All') { + Write-Verbose "Adding $($PSBoundParameters[$Property])" + foreach ($v in $PSBoundParameters[$Property]) { + $Hash.Add($v,1) } - 'Job' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_.Id}) -join '|') - [void]$WhereList.Add("`$_.id -match $Items") - } - 'Batch' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_}) -join '|') - [void]$WhereList.Add("`$_.batch -match $Items") - } } - If ($PSBoundParameters['State']) { - [void]$WhereList.Add("`$_.State -match `"$($State -join '|')`"") + } + End { + #Job objects searched by ID + if ($Property -eq 'Job') { $Property = 'ID' } + # IF faster than any scriptblocks + if ($PSCmdlet.ParameterSetName -eq 'All') { + $ResultJobs = $PoshRS_Jobs } - If ($PSBoundParameters.ContainsKey('HasMoreData')) { - [void]$WhereList.Add("`$_.HasMoreData -eq `$$HasMoreData") + else { + [array]$ResultJobs = foreach ($job in $PoshRS_Jobs) { + if ($Hash.ContainsKey($job.$Property)) { + $job + } + } } - Write-Debug "WhereListCount: $($WhereList.count)" - If ($WhereList.count -gt 0) { - $WhereString = $WhereList -join ' -AND ' - $WhereBlock = [scriptblock]::Create($WhereString) - Write-Debug "WhereString: $($WhereString)" - Write-Verbose "Using scriptblock" - $PoshRS_Jobs | Where-Object $WhereBlock - } Else { - $PoshRS_Jobs + if ($ResultJobs.Count -and $PSBoundParameters.ContainsKey('State')) { + $States = '^' + $State -join '$|^' + '$' + [array]$ResultJobs = foreach ($job in $ResultJobs) { + if ($job.State -match $States) { + $job + } + } + } + if ($ResultJobs.Count -and $PSBoundParameters.ContainsKey('HasMoreData')) { + [array]$ResultJobs = foreach ($job in $ResultJobs) { + if ($job.HasMoreData -eq $HasMoreData) { + $job + } + } } + $ResultJobs } } diff --git a/PoshRSJob/Public/Receive-RSJob.ps1 b/PoshRSJob/Public/Receive-RSJob.ps1 index 6817fec..d66b05a 100644 --- a/PoshRSJob/Public/Receive-RSJob.ps1 +++ b/PoshRSJob/Public/Receive-RSJob.ps1 @@ -7,8 +7,8 @@ Function Receive-RSJob { Gets the results of the Windows PowerShell runspace jobs in the current session. You can use Get-RSJob and pipe the results into this function to get the results as well. - .PARAMETER InputObject - Represents the RSJob object being sent to command. + .PARAMETER Job + Represents the RSJob object to receive available data from. .PARAMETER Name The name of the jobs to receive available data from. @@ -17,14 +17,14 @@ Function Receive-RSJob { The ID of the jobs to receive available data from. .PARAMETER InstanceID - The GUID of the jobs to receive available data from. - - .PARAMETER Batch - Name of the set of jobs + The GUID of the jobs to receive available data from. + + .PARAMETER Batch + Name of the set of jobs to receive available data from. .NOTES Name: Receive-RSJob - Author: Boe Prox + Author: Boe Prox/Max Kozlov .EXAMPLE Get-RSJob -State Completed | Receive-RSJob @@ -51,117 +51,47 @@ Function Receive-RSJob { DefaultParameterSetName='Job' )] Param ( - [parameter(Position=0,ValueFromPipeline=$True,ParameterSetName='Job')] - [RSJob[]]$InputObject, - [parameter(Position=1,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, + [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, + ParameterSetName='Job', Position=0)] + [Alias('InputObject')] + [RSJob[]]$Job, + [parameter(ValueFromPipelineByPropertyName=$True, ParameterSetName='Name')] [string[]]$Name, - [parameter(Position=2,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, + [parameter(ValueFromPipelineByPropertyName=$True, ParameterSetName='Id')] [int[]]$Id, - [parameter(Position=3,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, - ParameterSetName='Guid')] - [guid[]]$InstanceID, - [parameter(Position=4,ValueFromPipelineByPropertyName=$True, + [parameter(ValueFromPipelineByPropertyName=$True, + ParameterSetName='InstanceID')] + [string[]]$InstanceID, + [parameter(ValueFromPipelineByPropertyName=$True, ParameterSetName='Batch')] [string[]]$Batch ) Begin { If ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' - } - $List = New-Object System.Collections.ArrayList - $StringBuilder = New-Object System.Text.StringBuilder - $Bound = $False - $IsInputObject = $False - - #Take care of bound parameters - If ($PSBoundParameters['Name']) { - [void]$list.AddRange($Name) - $Bound = $True - } - If ($PSBoundParameters['Id']) { - [void]$list.AddRange($Id) - $Bound = $True - } - If ($PSBoundParameters['InstanceId']) { - [void]$list.AddRange($InstanceId) - $Bound = $True - } - If ($PSBoundParameters['InputObject']) { - $IsInputObject = $True - [void]$list.AddRange($InputObject) - $Bound = $True - } - If ($PSBoundParameters['Batch']) { - [void]$list.AddRange($Batch) - $Bound = $True } - Write-Debug "Bound: $Bound" + $List = New-Object System.Collections.ArrayList } Process { - If (-NOT $Bound -and $InputObject) { - $IsInputObject = $True - $_ | WriteStream - if (@("Completed", "Failed", "Stopped") -contains $_.State) { - $_ | SetIsReceived -SetTrue - } - } - elseif (-Not $Bound) { - [void]$List.Add($_) - } + Write-Debug "ParameterSet: $($PSCmdlet.ParameterSetName)" + $Property = $PSCmdlet.ParameterSetName + Write-Verbose "Adding $($PSBoundParameters[$Property])" + [void]$List.AddRange($PSBoundParameters[$Property]) } End { - Write-Debug "ParameterSet: $($PSCmdlet.parametersetname)" - Switch ($PSCmdlet.parametersetname) { - 'Name' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_}) -join '|') -replace '\*','.*' - [void]$StringBuilder.Append("`$_.Name -match $Items") - $ScriptBlock = [scriptblock]::Create($StringBuilder.ToString()) - } - 'Id' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_}) -join '|') - [void]$StringBuilder.Append("`$_.Id -match $Items") - $ScriptBlock = [scriptblock]::Create($StringBuilder.ToString()) - } - 'Guid' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_}) -join '|') - [void]$StringBuilder.Append("`$_.InstanceId -match $Items") - $ScriptBlock = [scriptblock]::Create($StringBuilder.ToString()) - } - 'Batch' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_}) -join '|') - [void]$StringBuilder.Append("`$_.batch -match $Items") - $ScriptBlock = [scriptblock]::Create($StringBuilder.ToString()) - } - Default {$ScriptBlock=$Null} - } - Write-Debug "ScriptBlock: $($ScriptBlock)" - If ($ScriptBlock) { - Write-Debug "Running Scriptblock" - $PoshRS_jobs | Where-Object $ScriptBlock | ForEach-Object{ - $_ | WriteStream - if (@("Completed", "Failed", "Stopped") -contains $_.State) { - $_ | SetIsReceived -SetTrue - } - } - } - ElseIf ($IsInputObject) { - $List | ForEach-Object{ - $_ | WriteStream - if (@("Completed", "Failed", "Stopped") -contains $_.State) { - $_ | SetIsReceived -SetTrue - } - } - } - ElseIf ($Bound) { - $PoshRS_jobs | ForEach-Object{ + $PSBoundParameters[$Property] = $List + if (-not $List.Count) { return } # No jobs selected to search + [array]$ToReceive = Get-RSJob @PSBoundParameters + + if ($ToReceive.Count) { + $ToReceive | ForEach-Object{ $_ | WriteStream - if (@("Completed", "Failed", "Stopped") -contains $_.State) { + if (@("Completed", "Failed", "Stopped") -contains $_.State) { $_ | SetIsReceived -SetTrue } } } } } - diff --git a/PoshRSJob/Public/Remove-RSJob.ps1 b/PoshRSJob/Public/Remove-RSJob.ps1 index 2ad41ce..c243c0f 100644 --- a/PoshRSJob/Public/Remove-RSJob.ps1 +++ b/PoshRSJob/Public/Remove-RSJob.ps1 @@ -6,6 +6,9 @@ Function Remove-RSJob { .DESCRIPTION Deletes a Windows PowerShell background job that has been started using Start-RSJob + .PARAMETER Job + The job object to remove. + .PARAMETER Name The name of the jobs to remove.. @@ -15,18 +18,15 @@ Function Remove-RSJob { .PARAMETER InstanceID The GUID of the jobs to remove. - .PARAMETER Batch - Name of the set of jobs - - .PARAMETER Job - The job object to remove. - + .PARAMETER Batch + Name of the set of jobs to remove. + .PARAMETER Force - Force a running job to stop prior to being removed. + Force a running job to stop prior to being removed. .NOTES Name: Remove-RSJob - Author: Boe Prox + Author: Boe Prox/Max Kozlov .EXAMPLE Get-RSJob -State Completed | Remove-RSJob @@ -48,103 +48,59 @@ Function Remove-RSJob { )] Param ( [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, + ParameterSetName='Job', Position=0)] + [Alias('InputObject')] + [RSJob[]]$Job, + [parameter(ValueFromPipelineByPropertyName=$True, ParameterSetName='Name')] [string[]]$Name, - [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, + [parameter(ValueFromPipelineByPropertyName=$True, ParameterSetName='Id')] [int[]]$Id, - [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, - ParameterSetName='Guid')] - [guid[]]$InstanceID, + [parameter(ValueFromPipelineByPropertyName=$True, + ParameterSetName='InstanceID')] + [string[]]$InstanceID, [parameter(ValueFromPipelineByPropertyName=$True, ParameterSetName='Batch')] [string[]]$Batch, - [parameter(ValueFromPipeline=$True,ParameterSetName='Job')] - [RSJob[]]$Job, + [parameter()] [switch]$Force ) - Begin { + Begin { If ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' - } - $List = New-Object System.Collections.ArrayList - $StringBuilder = New-Object System.Text.StringBuilder - - #Take care of bound parameters - $Bound = $False - If ($PSBoundParameters['Name']) { - [void]$list.AddRange($Name) - $Bound = $True - } - If ($PSBoundParameters['Batch']) { - [void]$list.AddRange($Batch) - $Bound = $True - } - If ($PSBoundParameters['Id']) { - [void]$list.AddRange($Id) - $Bound = $True - } - If ($PSBoundParameters['InstanceId']) { - [void]$list.AddRange($InstanceId) - $Bound = $True - } - If ($PSBoundParameters['Job']) { - [void]$list.AddRange($Job) - $Bound = $True } + $List = New-Object System.Collections.ArrayList } Process { - If (-Not $Bound) { - [void]$List.Add($_) - } + Write-Debug "ParameterSet: $($PSCmdlet.ParameterSetName)" + $Property = $PSCmdlet.ParameterSetName + Write-Verbose "Adding $($PSBoundParameters[$Property])" + [void]$List.AddRange($PSBoundParameters[$Property]) } End { - Write-Debug "ParameterSet: $($PSCmdlet.parametersetname)" - Switch ($PSCmdlet.parametersetname) { - 'Name' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_}) -join '|') -replace '\*','.*' - [void]$StringBuilder.Append("`$_.Name -match $Items") - $ScriptBlock = [scriptblock]::Create($StringBuilder.ToString()) - } - 'Id' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_}) -join '|') - [void]$StringBuilder.Append("`$_.Id -match $Items") - $ScriptBlock = [scriptblock]::Create($StringBuilder.ToString()) - } - 'Guid' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_}) -join '|') - [void]$StringBuilder.Append("`$_.InstanceId -match $Items") - $ScriptBlock = [scriptblock]::Create($StringBuilder.ToString()) - } - 'Batch' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_}) -join '|') - [void]$StringBuilder.Append("`$_.batch -match $Items") - $ScriptBlock = [scriptblock]::Create($StringBuilder.ToString()) - } - Default {$ScriptBlock=$Null} - } - If ($ScriptBlock) { - Write-Verbose "Using ScriptBlock" - $ToRemove = $PoshRS_jobs | Where-Object $ScriptBlock - } Else { - $ToRemove = $List - } - [System.Threading.Monitor]::Enter($PoshRS_Jobs.syncroot) - $ToRemove | ForEach-Object { - If ($PSCmdlet.ShouldProcess("Name: $($_.Name), associated with JobID $($_.Id)",'Remove')) { - If ($_.State -notmatch 'Completed|Failed|Stopped') { - If ($PSBoundParameters.ContainsKey('Force')) { - [void] $_.InnerJob.Stop() - $PoshRS_Jobs.Remove($_) + $PSBoundParameters[$Property] = $List + if (-not $List.Count) { return } # No jobs selected to search + [void]$PSBoundParameters.Remove('Force') + [array]$ToRemove = Get-RSJob @PSBoundParameters + if ($ToRemove.Count) { + [System.Threading.Monitor]::Enter($PoshRS_Jobs.syncroot) + $ToRemove | ForEach-Object { + If ($PSCmdlet.ShouldProcess("Name: $($_.Name), associated with JobID $($_.Id)",'Remove')) { + If ($_.State -notmatch 'Completed|Failed|Stopped') { + If ($Force) { + [void] $_.InnerJob.Stop() + $PoshRS_Jobs.Remove($_) + } Else { + Write-Error "Unable to remove job $($_.InstanceID)" + } } Else { - Write-Error "Unable to remove job $($_.InstanceID)" + [void]$PoshRS_Jobs.Remove($_) } - } Else { - [void]$PoshRS_Jobs.Remove($_) } } + [System.Threading.Monitor]::Exit($PoshRS_Jobs.syncroot) } - [System.Threading.Monitor]::Exit($PoshRS_Jobs.syncroot) } } diff --git a/PoshRSJob/Public/Stop-RSJob.ps1 b/PoshRSJob/Public/Stop-RSJob.ps1 index e22bffb..cf5d874 100644 --- a/PoshRSJob/Public/Stop-RSJob.ps1 +++ b/PoshRSJob/Public/Stop-RSJob.ps1 @@ -6,6 +6,9 @@ Function Stop-RSJob { .DESCRIPTION Stops a Windows PowerShell background job that has been started using Start-RSJob + .PARAMETER Job + The job object to stop. + .PARAMETER Name The name of the jobs to stop.. @@ -14,16 +17,13 @@ Function Stop-RSJob { .PARAMETER InstanceID The GUID of the jobs to stop. - - .PARAMETER Job - The job object to stop. - - .PARAMETER Batch - Name of the set of jobs + + .PARAMETER Batch + Name of the set of jobs to stop. .NOTES Name: Stop-RSJob - Author: Boe Prox + Author: Boe Prox/Max Kozlov .EXAMPLE Get-RSJob -State Completed | Stop-RSJob @@ -44,92 +44,42 @@ Function Stop-RSJob { )] Param ( [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, + ParameterSetName='Job', Position=0)] + [Alias('InputObject')] + [RSJob[]]$Job, + [parameter(ValueFromPipelineByPropertyName=$True, ParameterSetName='Name')] [string[]]$Name, - [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, + [parameter(ValueFromPipelineByPropertyName=$True, ParameterSetName='Id')] [int[]]$Id, - [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, - ParameterSetName='Guid')] - [guid[]]$InstanceID, + [parameter(ValueFromPipelineByPropertyName=$True, + ParameterSetName='InstanceID')] + [string[]]$InstanceID, [parameter(ValueFromPipelineByPropertyName=$True, ParameterSetName='Batch')] - [string[]]$Batch, - [parameter(ValueFromPipeline=$True,ParameterSetName='Job')] - [RSJob[]]$Job + [string[]]$Batch ) - Begin { + Begin { If ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' - } - Write-Debug "Begin" - $List = New-Object System.Collections.ArrayList - $StringBuilder = New-Object System.Text.StringBuilder - - #Take care of bound parameters - $Bound = $False - If ($PSBoundParameters['Name']) { - [void]$list.AddRange($Name) - $Bound = $True - } - If ($PSBoundParameters['Id']) { - [void]$list.AddRange($Id) - $Bound = $True } - If ($PSBoundParameters['InstanceId']) { - [void]$list.AddRange($InstanceId) - $Bound = $True - } - If ($PSBoundParameters['Job']) { - [void]$list.AddRange($Job) - $Bound = $True - } - If ($PSBoundParameters['Batch']) { - [void]$list.AddRange($Batch) - $Bound = $True - } - Write-Debug "Process" + $List = New-Object System.Collections.ArrayList } Process { - If (-Not $Bound) { - [void]$List.Add($_) - } + Write-Debug "ParameterSet: $($PSCmdlet.ParameterSetName)" + $Property = $PSCmdlet.ParameterSetName + Write-Verbose "Adding $($PSBoundParameters[$Property])" + [void]$List.AddRange($PSBoundParameters[$Property]) } End { - Write-Debug "End" - Write-Debug "ParameterSet: $($PSCmdlet.parametersetname)" - Switch ($PSCmdlet.parametersetname) { - 'Name' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_}) -join '|') -replace '\*','.*' - [void]$StringBuilder.Append("`$_.Name -match $Items") - $ScriptBlock = [scriptblock]::Create($StringBuilder.ToString()) - } - 'Id' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_}) -join '|') - [void]$StringBuilder.Append("`$_.Id -match $Items") - $ScriptBlock = [scriptblock]::Create($StringBuilder.ToString()) - } - 'Guid' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_}) -join '|') - [void]$StringBuilder.Append("`$_.InstanceId -match $Items") - $ScriptBlock = [scriptblock]::Create($StringBuilder.ToString()) - } - 'Batch' { - $Items = '"{0}"' -f (($list | ForEach-Object {"^{0}$" -f $_}) -join '|') - [void]$StringBuilder.Append("`$_.batch -match $Items") - $ScriptBlock = [scriptblock]::Create($StringBuilder.ToString()) - } - Default {$ScriptBlock=$Null} - } - If ($ScriptBlock) { - Write-Verbose "Using ScriptBlock" - $ToStop = $PoshRS_jobs | Where-Object $ScriptBlock - } Else { - $ToStop = $List - } - If ($ToStop) { - [System.Threading.Monitor]::Enter($PoshRS_jobs.syncroot) - $ToStop | ForEach-Object { + $PSBoundParameters[$Property] = $List + if (-not $List.Count) { return } # No jobs selected to search + [array]$ToStop = Get-RSJob @PSBoundParameters + + If ($ToStop.Count) { + [System.Threading.Monitor]::Enter($PoshRS_jobs.syncroot) + $ToStop | ForEach-Object { Write-Verbose "Stopping $($_.InstanceId)" if ($_.State -ne 'Completed') { Write-Verbose "Killing job $($_.InstanceId)" @@ -138,5 +88,5 @@ Function Stop-RSJob { } [System.Threading.Monitor]::Exit($PoshRS_jobs.syncroot) } - } + } } diff --git a/PoshRSJob/Public/Wait-RSJob.ps1 b/PoshRSJob/Public/Wait-RSJob.ps1 index 65d3e0b..6755754 100644 --- a/PoshRSJob/Public/Wait-RSJob.ps1 +++ b/PoshRSJob/Public/Wait-RSJob.ps1 @@ -1,13 +1,16 @@ Function Wait-RSJob { <# .SYNOPSIS - Waits until all RSJobs are in one of the following states: + Waits until all RSJobs are in one of the following states: .DESCRIPTION Waits until all RSJobs are in one of the following states: + .PARAMETER Job + The job object to wait for. + .PARAMETER Name - The name of the jobs to query for. + The name of the jobs to wait for. .PARAMETER ID The ID of the jobs that you want to wait for. @@ -26,7 +29,7 @@ Function Wait-RSJob { Disconnected .PARAMETER Batch - Name tof the set of RSJobs + Name of the set of jobs that you want to wait for. .PARAMETER HasMoreData Waits for jobs that have data being outputted. You can specify -HasMoreData:$False to wait for jobs @@ -47,13 +50,15 @@ Function Wait-RSJob { Get-RSJob | Wait-RSJob Description ----------- - Waits for jobs which have to be completed. + Waits for jobs which have to be completed. #> [cmdletbinding( DefaultParameterSetName='All' )] Param ( - [parameter(ValueFromPipeline=$True,ParameterSetName='Job')] + [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, + ParameterSetName='Job', Position=0)] + [Alias('InputObject')] [RSJob[]]$Job, [parameter(ValueFromPipelineByPropertyName=$True, ParameterSetName='Name')] @@ -67,6 +72,7 @@ Function Wait-RSJob { [parameter(ValueFromPipelineByPropertyName=$True, ParameterSetName='Batch')] [string[]]$Batch, + [parameter(ParameterSetName='Batch')] [parameter(ParameterSetName='Name')] [parameter(ParameterSetName='Id')] @@ -74,67 +80,32 @@ Function Wait-RSJob { [parameter(ParameterSetName='All')] [ValidateSet('NotStarted','Running','Completed','Failed','Stopping','Stopped','Disconnected')] [string[]]$State, - [parameter(ParameterSetName='Batch')] - [parameter(ParameterSetName='Name')] - [parameter(ParameterSetName='Id')] - [parameter(ParameterSetName='InstanceID')] - [parameter(ParameterSetName='All')] - [Switch]$HasMoreData, [int]$Timeout, [switch]$ShowProgress ) Begin { If ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' - } - $WaitJobs = New-Object System.Collections.ArrayList - $Hash = @{} + } + $List = New-Object System.Collections.ArrayList } Process { Write-Debug "ParameterSet: $($PSCmdlet.ParameterSetName)" $Property = $PSCmdlet.ParameterSetName - if ($PSCmdlet.ParameterSetName -eq 'Job') { - [void]$WaitJobs.AddRange($Job) - } - elseif ($PSCmdlet.ParameterSetName -ne 'All') { + if ($PSCmdlet.ParameterSetName -ne 'All') { Write-Verbose "Adding $($PSBoundParameters[$Property])" - foreach ($v in $PSBoundParameters[$Property]) { - $Hash.Add($v,1) - } + [void]$List.AddRange($PSBoundParameters[$Property]) } } End { - # IF faster than any scriptblocks - if ($PSCmdlet.ParameterSetName -ne 'Job') { - if ($PSCmdlet.ParameterSetName -eq 'All') { - if ($PSBoundParameters.ContainsKey('State') -or - $PSBoundParameters.ContainsKey('HasMoreData')) { - $WaitJobs = $PoshRS_Jobs - } - } - else { - foreach ($job in $PoshRS_Jobs) { - if ($Hash.ContainsKey($job.$Property)) { - [void]$WaitJobs.Add($job) - } - } - } - } - if ($WaitJobs.Count -and $PSBoundParameters.ContainsKey('State')) { - $States = '^' + $State -join '$|^' + '$' - [array]$WaitJobs = foreach ($job in $WaitJobs) { - if ($job.State -match $States) { - $job - } - } - } - if ($WaitJobs.Count -and $PSBoundParameters.ContainsKey('HasMoreData')) { - [array]$WaitJobs = foreach ($job in $WaitJobs) { - if ($job.HasMoreData -eq $HasMoreData) { - $job - } - } + if ($PSCmdlet.ParameterSetName -ne 'All') { + $PSBoundParameters[$Property] = $List } + if (-not $List.Count) { return } # No jobs selected to search + [void]$PSBoundParameters.Remove('Timeout') + [void]$PSBoundParameters.Remove('ShowProgress') + [array]$WaitJobs = Get-RSJob @PSBoundParameters + $TotalJobs = $WaitJobs.Count $Completed = 0 Write-Verbose "Wait for $($TotalJobs) jobs" @@ -160,7 +131,7 @@ Function Wait-RSJob { Write-Debug "Completed: ($Completed)" Write-Debug "Total: ($Totaljobs)" Write-Debug "Status: $($Completed/$TotalJobs)" - If ($PSBoundParameters.ContainsKey('ShowProgress')) { + If ($ShowProgress) { Write-Progress -Activity "RSJobs Tracker" -Status ("Remaining Jobs: {0}" -f $Waitjobs.Count) -PercentComplete (($Completed/$TotalJobs)*100) } if($Timeout -and (New-Timespan $Date).TotalSeconds -ge $Timeout){ diff --git a/Tests/PoshRSJob.Tests.ps1 b/Tests/PoshRSJob.Tests.ps1 index 2c1ea8f..9d20218 100644 --- a/Tests/PoshRSJob.Tests.ps1 +++ b/Tests/PoshRSJob.Tests.ps1 @@ -59,7 +59,7 @@ Describe "PoshRSJob PS$($PSVersion)" { $Commands -contains "wsj" | Should be $True } It 'should initialize necessary variables' { - $PSCmdlet.SessionState.PSVariable.Get('PoshRS_runspacepools').Name | Should Be 'PoshRS_RunspacePools' + $PSCmdlet.SessionState.PSVariable.Get('PoshRS_RunspacePools').Name | Should Be 'PoshRS_RunspacePools' $PSCmdlet.SessionState.PSVariable.Get('PoshRS_RunspacePoolCleanup').Name | Should Be 'PoshRS_RunspacePoolCleanup' $PSCmdlet.SessionState.PSVariable.Get('PoshRS_JobCleanup').Name | Should Be 'PoshRS_JobCleanup' $PSCmdlet.SessionState.PSVariable.Get('PoshRS_JobID').Name | Should Be 'PoshRS_JobID' @@ -67,7 +67,7 @@ Describe "PoshRSJob PS$($PSVersion)" { } } } - + Describe "Start-RSJob PS$PSVersion" { Context 'Strict mode' { Set-StrictMode -Version latest @@ -77,9 +77,9 @@ Describe "Start-RSJob PS$PSVersion" { $SecondJob = Start-RSJob {$Null} $NextID = $SecondJob.Id $NextID - $InitialID | Should Be 1 - + } - It 'should return initial job details' { + It 'should return initial job details' { $Output1 = @( 1 | Start-RSJob @Verbose -ScriptBlock { Param($Object) $Object @@ -90,7 +90,7 @@ Describe "Start-RSJob PS$PSVersion" { } ) $Output1.Count | Should be 1 $Output5.Count | Should be 5 - } + } It 'should support $using syntax' { $Test = "5" $Output1 = 1 | Start-RSJob @Verbose -ScriptBlock { @@ -103,7 +103,7 @@ Describe "Start-RSJob PS$PSVersion" { $_ } ) | Wait-RSJob | Receive-RSJob $Output1 | Should Be 1 - } + } } } @@ -120,23 +120,23 @@ Describe "Stop-RSJob PS$PSVersion" { } } } - + Describe "Get-RSJob PS$PSVersion" { Context 'Strict mode' { Set-StrictMode -Version latest It 'should return all job details' { $Output = @( Get-RSJob @Verbose ) $Props = $Output[0].PSObject.Properties | Select-Object -ExpandProperty Name - + $Output.count | Should be 11 $Props -contains "Id" | Should be $True $Props -contains "State" | Should be $True $Props -contains "HasMoreData" | Should be $True - } + } It 'should return job details based on ID' { $Output = @( Get-RSJob @Verbose -Id 1 ) $Props = $Output[0].PSObject.Properties | Select-Object -ExpandProperty Name - + $Output.count | Should be 1 $Props -contains "Id" | Should be $True $Props -contains "State" | Should be $True @@ -145,7 +145,7 @@ Describe "Get-RSJob PS$PSVersion" { It 'should return job details based on Name' { $Output = @( Get-RSJob @Verbose -Name Job2 ) $Props = $Output[0].PSObject.Properties | Select-Object -ExpandProperty Name - + $Output.count | Should be 1 $Props -contains "Id" | Should be $True $Props -contains "State" | Should be $True @@ -153,14 +153,14 @@ Describe "Get-RSJob PS$PSVersion" { } } } - + Describe "Remove-RSJob PS$PSVersion" { Context 'Strict mode' { Set-StrictMode -Version latest It 'should remove jobs' { Get-RSJob @Verbose | Remove-RSJob @Verbose $Output = @( Get-RSJob @Verbose ) - + $Output.count | Should be 0 } It 'should only remove specified jobs by ID' { @@ -185,9 +185,9 @@ Describe "Remove-RSJob PS$PSVersion" { #We only removed one $RemainingNames.count -eq ($AllNames.count - 1) | Should Be $True #We removed the right ID - $RemainingNames -notcontains $ThisJobName | Should Be $True + $RemainingNames -notcontains $ThisJobName | Should Be $True } - It 'should only remove specified jobs by InputObject' { + It 'should only remove specified jobs by InputObject' { $TestJobs = @( 1..5 | Start-RSJob @Verbose -ScriptBlock { "" }) Start-Sleep -Seconds 2 $ThisJob = $TestJobs[0] @@ -196,34 +196,34 @@ Describe "Remove-RSJob PS$PSVersion" { #We only removed one $RemainingNames.count -eq ($TestJobs.count - 1) | Should Be $True #We removed the right ID - $RemainingNames -notcontains $ThisJob.Name | Should Be $True + $RemainingNames -notcontains $ThisJob.Name | Should Be $True } } } - + Describe "Receive-RSJob PS$PSVersion" { Context 'Strict mode' { Set-StrictMode -Version latest It 'should retrieve job data' { $TestJob = 0 | Start-RSJob @Verbose -ScriptBlock {"Working on $_"} Start-Sleep -Seconds 1 - + $Output = @( $TestJob | Receive-RSJob @Verbose ) $Output.Count | Should be 1 $Output[0] | Should be "Working on 0" - + } It 'should not remove the job' { $TestJob = 0 | Start-RSJob @Verbose -ScriptBlock {""} Start-Sleep -Seconds 1 $TestJob | Receive-RSJob @Verbose | Out-Null - + $Output = @( $TestJob | Get-RSJob @Verbose ) $Output.Count | Should be 1 } } } - + Describe "Wait-RSJob PS$PSVersion" { Context 'Strict mode' { Set-StrictMode -Version latest @@ -234,7 +234,7 @@ Describe "Wait-RSJob PS$PSVersion" { Get-Date } $TestJob | Wait-RSJob # Omitted verbose to avoid clutter - $EndDate = Get-Date + $EndDate = Get-Date ( $EndDate - $StartDate ).TotalSeconds -gt 5 | Should be $True } It 'should wait for jobs by Name' { @@ -281,13 +281,13 @@ Describe "Test RSJob Throttling" { It "Full Pipe input" { $StartDate = Get-Date Test-RSJob $true - $EndDate = Get-Date + $EndDate = Get-Date ( $EndDate - $StartDate ).TotalSeconds -gt 25 | Should be $True } It "OneByOne Pipe input" { $StartDate = Get-Date Test-RSJob $false - $EndDate = Get-Date + $EndDate = Get-Date ( $EndDate - $StartDate ).TotalSeconds -gt 25 | Should be $True } } From 229e820d4bc1f33c97d9786dcc29446fde5fab33 Mon Sep 17 00:00:00 2001 From: Max Kozlov Date: Wed, 19 Apr 2017 16:24:34 +0300 Subject: [PATCH 07/17] Get-RSJob optimizations --- PoshRSJob/Public/Get-RSJob.ps1 | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/PoshRSJob/Public/Get-RSJob.ps1 b/PoshRSJob/Public/Get-RSJob.ps1 index 79ca9fc..31c1b16 100644 --- a/PoshRSJob/Public/Get-RSJob.ps1 +++ b/PoshRSJob/Public/Get-RSJob.ps1 @@ -103,6 +103,7 @@ Function Get-RSJob { } Write-Debug "ParameterSet: $($PSCmdlet.parametersetname)" $Hash = @{} + $ResultJobs = New-Object System.Collections.ArrayList } Process { Write-Debug "ParameterSet: $($PSCmdlet.ParameterSetName)" @@ -124,32 +125,29 @@ Function Get-RSJob { End { #Job objects searched by ID if ($Property -eq 'Job') { $Property = 'ID' } + $States = if ($PSBoundParameters.ContainsKey('State')) { '^' + ($State -join '$|^') + '$' } else { '.' } + # IF faster than any scriptblocks if ($PSCmdlet.ParameterSetName -eq 'All') { + Write-Verbose 'All Jobs' $ResultJobs = $PoshRS_Jobs } else { - [array]$ResultJobs = foreach ($job in $PoshRS_Jobs) { - if ($Hash.ContainsKey($job.$Property)) { - $job + Write-Verbose "Filtered Jobs by $Property" + foreach ($job in $PoshRS_Jobs) { + if ($Hash.ContainsKey($job.$Property)) + { + [void]$ResultJobs.Add($job) } } } - if ($ResultJobs.Count -and $PSBoundParameters.ContainsKey('State')) { - $States = '^' + $State -join '$|^' + '$' - [array]$ResultJobs = foreach ($job in $ResultJobs) { - if ($job.State -match $States) { - $job - } - } - } - if ($ResultJobs.Count -and $PSBoundParameters.ContainsKey('HasMoreData')) { - [array]$ResultJobs = foreach ($job in $ResultJobs) { - if ($job.HasMoreData -eq $HasMoreData) { - $job - } + foreach ($job in $ResultJobs) { + if (($job.State -match $States) -and + (-not $PSBoundParameters.ContainsKey('HasMoreData') -or $job.HasMoreData -eq $HasMoreData) + ) + { + $job } } - $ResultJobs } } From 8907933c277bd30e9f74b453c90cb23e17b3b279 Mon Sep 17 00:00:00 2001 From: Max Kozlov Date: Thu, 20 Apr 2017 00:37:41 +0300 Subject: [PATCH 08/17] Returned back pipeline support for job ID and Name --- PoshRSJob/Public/Get-RSJob.ps1 | 2 +- PoshRSJob/Public/Receive-RSJob.ps1 | 4 ++-- PoshRSJob/Public/Remove-RSJob.ps1 | 4 ++-- PoshRSJob/Public/Stop-RSJob.ps1 | 4 ++-- PoshRSJob/Public/Wait-RSJob.ps1 | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/PoshRSJob/Public/Get-RSJob.ps1 b/PoshRSJob/Public/Get-RSJob.ps1 index 31c1b16..500e822 100644 --- a/PoshRSJob/Public/Get-RSJob.ps1 +++ b/PoshRSJob/Public/Get-RSJob.ps1 @@ -70,7 +70,7 @@ Function Get-RSJob { ParameterSetName='Job', Position=0)] [Alias('InputObject')] [RSJob[]]$Job, - [parameter(ValueFromPipelineByPropertyName=$True, + [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, ParameterSetName='Name')] [string[]]$Name, [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, diff --git a/PoshRSJob/Public/Receive-RSJob.ps1 b/PoshRSJob/Public/Receive-RSJob.ps1 index d66b05a..c660499 100644 --- a/PoshRSJob/Public/Receive-RSJob.ps1 +++ b/PoshRSJob/Public/Receive-RSJob.ps1 @@ -55,10 +55,10 @@ Function Receive-RSJob { ParameterSetName='Job', Position=0)] [Alias('InputObject')] [RSJob[]]$Job, - [parameter(ValueFromPipelineByPropertyName=$True, + [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, ParameterSetName='Name')] [string[]]$Name, - [parameter(ValueFromPipelineByPropertyName=$True, + [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, ParameterSetName='Id')] [int[]]$Id, [parameter(ValueFromPipelineByPropertyName=$True, diff --git a/PoshRSJob/Public/Remove-RSJob.ps1 b/PoshRSJob/Public/Remove-RSJob.ps1 index c243c0f..8ba2f33 100644 --- a/PoshRSJob/Public/Remove-RSJob.ps1 +++ b/PoshRSJob/Public/Remove-RSJob.ps1 @@ -51,10 +51,10 @@ Function Remove-RSJob { ParameterSetName='Job', Position=0)] [Alias('InputObject')] [RSJob[]]$Job, - [parameter(ValueFromPipelineByPropertyName=$True, + [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, ParameterSetName='Name')] [string[]]$Name, - [parameter(ValueFromPipelineByPropertyName=$True, + [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, ParameterSetName='Id')] [int[]]$Id, [parameter(ValueFromPipelineByPropertyName=$True, diff --git a/PoshRSJob/Public/Stop-RSJob.ps1 b/PoshRSJob/Public/Stop-RSJob.ps1 index cf5d874..85cbaa3 100644 --- a/PoshRSJob/Public/Stop-RSJob.ps1 +++ b/PoshRSJob/Public/Stop-RSJob.ps1 @@ -47,10 +47,10 @@ Function Stop-RSJob { ParameterSetName='Job', Position=0)] [Alias('InputObject')] [RSJob[]]$Job, - [parameter(ValueFromPipelineByPropertyName=$True, + [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, ParameterSetName='Name')] [string[]]$Name, - [parameter(ValueFromPipelineByPropertyName=$True, + [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, ParameterSetName='Id')] [int[]]$Id, [parameter(ValueFromPipelineByPropertyName=$True, diff --git a/PoshRSJob/Public/Wait-RSJob.ps1 b/PoshRSJob/Public/Wait-RSJob.ps1 index 6755754..4575c7a 100644 --- a/PoshRSJob/Public/Wait-RSJob.ps1 +++ b/PoshRSJob/Public/Wait-RSJob.ps1 @@ -60,10 +60,10 @@ Function Wait-RSJob { ParameterSetName='Job', Position=0)] [Alias('InputObject')] [RSJob[]]$Job, - [parameter(ValueFromPipelineByPropertyName=$True, + [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, ParameterSetName='Name')] [string[]]$Name, - [parameter(ValueFromPipelineByPropertyName=$True, + [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, ParameterSetName='Id')] [int[]]$Id, [parameter(ValueFromPipelineByPropertyName=$True, From 282284feb6148b30da8902b602ba3915d4c9b035 Mon Sep 17 00:00:00 2001 From: Max Kozlov Date: Thu, 20 Apr 2017 09:40:56 +0300 Subject: [PATCH 09/17] Added positional parameters support for Job,ID,Name --- PoshRSJob/Public/Get-RSJob.ps1 | 4 ++-- PoshRSJob/Public/Receive-RSJob.ps1 | 4 ++-- PoshRSJob/Public/Remove-RSJob.ps1 | 4 ++-- PoshRSJob/Public/Stop-RSJob.ps1 | 4 ++-- PoshRSJob/Public/Wait-RSJob.ps1 | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/PoshRSJob/Public/Get-RSJob.ps1 b/PoshRSJob/Public/Get-RSJob.ps1 index 500e822..d336f9f 100644 --- a/PoshRSJob/Public/Get-RSJob.ps1 +++ b/PoshRSJob/Public/Get-RSJob.ps1 @@ -71,10 +71,10 @@ Function Get-RSJob { [Alias('InputObject')] [RSJob[]]$Job, [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, - ParameterSetName='Name')] + ParameterSetName='Name', Position=0)] [string[]]$Name, [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, - ParameterSetName='Id')] + ParameterSetName='Id', Position=0)] [int[]]$Id, [parameter(ValueFromPipelineByPropertyName=$True, ParameterSetName='InstanceID')] diff --git a/PoshRSJob/Public/Receive-RSJob.ps1 b/PoshRSJob/Public/Receive-RSJob.ps1 index c660499..379d271 100644 --- a/PoshRSJob/Public/Receive-RSJob.ps1 +++ b/PoshRSJob/Public/Receive-RSJob.ps1 @@ -56,10 +56,10 @@ Function Receive-RSJob { [Alias('InputObject')] [RSJob[]]$Job, [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, - ParameterSetName='Name')] + ParameterSetName='Name', Position=0)] [string[]]$Name, [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, - ParameterSetName='Id')] + ParameterSetName='Id', Position=0)] [int[]]$Id, [parameter(ValueFromPipelineByPropertyName=$True, ParameterSetName='InstanceID')] diff --git a/PoshRSJob/Public/Remove-RSJob.ps1 b/PoshRSJob/Public/Remove-RSJob.ps1 index 8ba2f33..23cbda3 100644 --- a/PoshRSJob/Public/Remove-RSJob.ps1 +++ b/PoshRSJob/Public/Remove-RSJob.ps1 @@ -52,10 +52,10 @@ Function Remove-RSJob { [Alias('InputObject')] [RSJob[]]$Job, [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, - ParameterSetName='Name')] + ParameterSetName='Name', Position=0)] [string[]]$Name, [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, - ParameterSetName='Id')] + ParameterSetName='Id', Position=0)] [int[]]$Id, [parameter(ValueFromPipelineByPropertyName=$True, ParameterSetName='InstanceID')] diff --git a/PoshRSJob/Public/Stop-RSJob.ps1 b/PoshRSJob/Public/Stop-RSJob.ps1 index 85cbaa3..a3ae727 100644 --- a/PoshRSJob/Public/Stop-RSJob.ps1 +++ b/PoshRSJob/Public/Stop-RSJob.ps1 @@ -48,10 +48,10 @@ Function Stop-RSJob { [Alias('InputObject')] [RSJob[]]$Job, [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, - ParameterSetName='Name')] + ParameterSetName='Name', Position=0)] [string[]]$Name, [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, - ParameterSetName='Id')] + ParameterSetName='Id', Position=0)] [int[]]$Id, [parameter(ValueFromPipelineByPropertyName=$True, ParameterSetName='InstanceID')] diff --git a/PoshRSJob/Public/Wait-RSJob.ps1 b/PoshRSJob/Public/Wait-RSJob.ps1 index 4575c7a..9b561fa 100644 --- a/PoshRSJob/Public/Wait-RSJob.ps1 +++ b/PoshRSJob/Public/Wait-RSJob.ps1 @@ -61,10 +61,10 @@ Function Wait-RSJob { [Alias('InputObject')] [RSJob[]]$Job, [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, - ParameterSetName='Name')] + ParameterSetName='Name', Position=0)] [string[]]$Name, [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True, - ParameterSetName='Id')] + ParameterSetName='Id', Position=0)] [int[]]$Id, [parameter(ValueFromPipelineByPropertyName=$True, ParameterSetName='InstanceID')] From 7d29d3a468d21b4b38ae769d729237dc3032acbf Mon Sep 17 00:00:00 2001 From: Max Kozlov Date: Thu, 20 Apr 2017 10:36:21 +0300 Subject: [PATCH 10/17] Allow brackets and spaces inside job names --- PoshRSJob/Public/Start-RSJob.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PoshRSJob/Public/Start-RSJob.ps1 b/PoshRSJob/Public/Start-RSJob.ps1 index 2ff9c86..a8a79ad 100644 --- a/PoshRSJob/Public/Start-RSJob.ps1 +++ b/PoshRSJob/Public/Start-RSJob.ps1 @@ -190,8 +190,8 @@ Function Start-RSJob { } If ($PSBoundParameters.ContainsKey('Name')) { If ($Name -isnot [scriptblock]) { - $JobName = [scriptblock]::Create("Write-Output $Name") - } + $JobName = [scriptblock]::Create("Write-Output `"$Name`"") + } Else { $JobName = [scriptblock]::Create( ($Name -replace '\$_','$Item')) } From 35068ba7498b03e6ced6398537b9f0e9ed49c608 Mon Sep 17 00:00:00 2001 From: Max Kozlov Date: Thu, 20 Apr 2017 11:56:13 +0300 Subject: [PATCH 11/17] Fixed hangup after Remove-RSJob -Force --- PoshRSJob/Public/Remove-RSJob.ps1 | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/PoshRSJob/Public/Remove-RSJob.ps1 b/PoshRSJob/Public/Remove-RSJob.ps1 index 23cbda3..8532ad4 100644 --- a/PoshRSJob/Public/Remove-RSJob.ps1 +++ b/PoshRSJob/Public/Remove-RSJob.ps1 @@ -86,21 +86,25 @@ Function Remove-RSJob { [array]$ToRemove = Get-RSJob @PSBoundParameters if ($ToRemove.Count) { [System.Threading.Monitor]::Enter($PoshRS_Jobs.syncroot) - $ToRemove | ForEach-Object { - If ($PSCmdlet.ShouldProcess("Name: $($_.Name), associated with JobID $($_.Id)",'Remove')) { - If ($_.State -notmatch 'Completed|Failed|Stopped') { - If ($Force) { - [void] $_.InnerJob.Stop() - $PoshRS_Jobs.Remove($_) + try { + $ToRemove | ForEach-Object { + If ($PSCmdlet.ShouldProcess("Name: $($_.Name), associated with JobID $($_.Id)",'Remove')) { + If ($_.State -notmatch 'Completed|Failed|Stopped') { + If ($Force) { + [void] $_.InnerJob.Stop() + $PoshRS_Jobs.Remove($_) + } Else { + Write-Error "Unable to remove job $($_.InstanceID)" + } } Else { - Write-Error "Unable to remove job $($_.InstanceID)" + [void]$PoshRS_Jobs.Remove($_) } - } Else { - [void]$PoshRS_Jobs.Remove($_) } } } - [System.Threading.Monitor]::Exit($PoshRS_Jobs.syncroot) + finally { + [System.Threading.Monitor]::Exit($PoshRS_Jobs.syncroot) + } } } } From 3143e9e6ed4117cc78f7b38d4f43b7e14f8d97d1 Mon Sep 17 00:00:00 2001 From: Max Kozlov Date: Thu, 20 Apr 2017 11:58:23 +0300 Subject: [PATCH 12/17] the same for Stop-RSJob --- PoshRSJob/Public/Stop-RSJob.ps1 | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/PoshRSJob/Public/Stop-RSJob.ps1 b/PoshRSJob/Public/Stop-RSJob.ps1 index a3ae727..fea880c 100644 --- a/PoshRSJob/Public/Stop-RSJob.ps1 +++ b/PoshRSJob/Public/Stop-RSJob.ps1 @@ -79,14 +79,18 @@ Function Stop-RSJob { If ($ToStop.Count) { [System.Threading.Monitor]::Enter($PoshRS_jobs.syncroot) - $ToStop | ForEach-Object { - Write-Verbose "Stopping $($_.InstanceId)" - if ($_.State -ne 'Completed') { - Write-Verbose "Killing job $($_.InstanceId)" - [void] $_.InnerJob.Stop() + try { + $ToStop | ForEach-Object { + Write-Verbose "Stopping $($_.InstanceId)" + if ($_.State -ne 'Completed') { + Write-Verbose "Killing job $($_.InstanceId)" + [void] $_.InnerJob.Stop() + } } } - [System.Threading.Monitor]::Exit($PoshRS_jobs.syncroot) + finally { + [System.Threading.Monitor]::Exit($PoshRS_jobs.syncroot) + } } } } From 4051bb90343762d2c417f2d21b41e6f202506858 Mon Sep 17 00:00:00 2001 From: Max Kozlov Date: Thu, 20 Apr 2017 12:16:40 +0300 Subject: [PATCH 13/17] Correctly return on empty search set --- PoshRSJob/Public/Receive-RSJob.ps1 | 2 +- PoshRSJob/Public/Remove-RSJob.ps1 | 2 +- PoshRSJob/Public/Stop-RSJob.ps1 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PoshRSJob/Public/Receive-RSJob.ps1 b/PoshRSJob/Public/Receive-RSJob.ps1 index 379d271..ffd2891 100644 --- a/PoshRSJob/Public/Receive-RSJob.ps1 +++ b/PoshRSJob/Public/Receive-RSJob.ps1 @@ -81,8 +81,8 @@ Function Receive-RSJob { [void]$List.AddRange($PSBoundParameters[$Property]) } End { - $PSBoundParameters[$Property] = $List if (-not $List.Count) { return } # No jobs selected to search + $PSBoundParameters[$Property] = $List [array]$ToReceive = Get-RSJob @PSBoundParameters if ($ToReceive.Count) { diff --git a/PoshRSJob/Public/Remove-RSJob.ps1 b/PoshRSJob/Public/Remove-RSJob.ps1 index 8532ad4..666c766 100644 --- a/PoshRSJob/Public/Remove-RSJob.ps1 +++ b/PoshRSJob/Public/Remove-RSJob.ps1 @@ -80,8 +80,8 @@ Function Remove-RSJob { [void]$List.AddRange($PSBoundParameters[$Property]) } End { - $PSBoundParameters[$Property] = $List if (-not $List.Count) { return } # No jobs selected to search + $PSBoundParameters[$Property] = $List [void]$PSBoundParameters.Remove('Force') [array]$ToRemove = Get-RSJob @PSBoundParameters if ($ToRemove.Count) { diff --git a/PoshRSJob/Public/Stop-RSJob.ps1 b/PoshRSJob/Public/Stop-RSJob.ps1 index fea880c..eee5c1a 100644 --- a/PoshRSJob/Public/Stop-RSJob.ps1 +++ b/PoshRSJob/Public/Stop-RSJob.ps1 @@ -73,8 +73,8 @@ Function Stop-RSJob { [void]$List.AddRange($PSBoundParameters[$Property]) } End { - $PSBoundParameters[$Property] = $List if (-not $List.Count) { return } # No jobs selected to search + $PSBoundParameters[$Property] = $List [array]$ToStop = Get-RSJob @PSBoundParameters If ($ToStop.Count) { From f0ddec2a59af4246629b9201cdb22881b3487679 Mon Sep 17 00:00:00 2001 From: Max Kozlov Date: Thu, 20 Apr 2017 13:03:04 +0300 Subject: [PATCH 14/17] Correctly return on empty search set #2 --- PoshRSJob/Public/Receive-RSJob.ps1 | 6 ++++-- PoshRSJob/Public/Remove-RSJob.ps1 | 6 ++++-- PoshRSJob/Public/Stop-RSJob.ps1 | 6 ++++-- PoshRSJob/Public/Wait-RSJob.ps1 | 2 +- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/PoshRSJob/Public/Receive-RSJob.ps1 b/PoshRSJob/Public/Receive-RSJob.ps1 index ffd2891..f217110 100644 --- a/PoshRSJob/Public/Receive-RSJob.ps1 +++ b/PoshRSJob/Public/Receive-RSJob.ps1 @@ -77,8 +77,10 @@ Function Receive-RSJob { Process { Write-Debug "ParameterSet: $($PSCmdlet.ParameterSetName)" $Property = $PSCmdlet.ParameterSetName - Write-Verbose "Adding $($PSBoundParameters[$Property])" - [void]$List.AddRange($PSBoundParameters[$Property]) + if ($PSBoundParameters[$Property]) { + Write-Verbose "Adding $($PSBoundParameters[$Property])" + [void]$List.AddRange($PSBoundParameters[$Property]) + } } End { if (-not $List.Count) { return } # No jobs selected to search diff --git a/PoshRSJob/Public/Remove-RSJob.ps1 b/PoshRSJob/Public/Remove-RSJob.ps1 index 666c766..65fe7bc 100644 --- a/PoshRSJob/Public/Remove-RSJob.ps1 +++ b/PoshRSJob/Public/Remove-RSJob.ps1 @@ -76,8 +76,10 @@ Function Remove-RSJob { Process { Write-Debug "ParameterSet: $($PSCmdlet.ParameterSetName)" $Property = $PSCmdlet.ParameterSetName - Write-Verbose "Adding $($PSBoundParameters[$Property])" - [void]$List.AddRange($PSBoundParameters[$Property]) + if ($PSBoundParameters[$Property]) { + Write-Verbose "Adding $($PSBoundParameters[$Property])" + [void]$List.AddRange($PSBoundParameters[$Property]) + } } End { if (-not $List.Count) { return } # No jobs selected to search diff --git a/PoshRSJob/Public/Stop-RSJob.ps1 b/PoshRSJob/Public/Stop-RSJob.ps1 index eee5c1a..8623dc4 100644 --- a/PoshRSJob/Public/Stop-RSJob.ps1 +++ b/PoshRSJob/Public/Stop-RSJob.ps1 @@ -69,8 +69,10 @@ Function Stop-RSJob { Process { Write-Debug "ParameterSet: $($PSCmdlet.ParameterSetName)" $Property = $PSCmdlet.ParameterSetName - Write-Verbose "Adding $($PSBoundParameters[$Property])" - [void]$List.AddRange($PSBoundParameters[$Property]) + if ($PSBoundParameters[$Property]) { + Write-Verbose "Adding $($PSBoundParameters[$Property])" + [void]$List.AddRange($PSBoundParameters[$Property]) + } } End { if (-not $List.Count) { return } # No jobs selected to search diff --git a/PoshRSJob/Public/Wait-RSJob.ps1 b/PoshRSJob/Public/Wait-RSJob.ps1 index 9b561fa..ebe4994 100644 --- a/PoshRSJob/Public/Wait-RSJob.ps1 +++ b/PoshRSJob/Public/Wait-RSJob.ps1 @@ -92,7 +92,7 @@ Function Wait-RSJob { Process { Write-Debug "ParameterSet: $($PSCmdlet.ParameterSetName)" $Property = $PSCmdlet.ParameterSetName - if ($PSCmdlet.ParameterSetName -ne 'All') { + if ($PSCmdlet.ParameterSetName -ne 'All' -and $PSBoundParameters[$Property]) { Write-Verbose "Adding $($PSBoundParameters[$Property])" [void]$List.AddRange($PSBoundParameters[$Property]) } From d6fe4535bfc471f364a110ce2339570dc9c6ce91 Mon Sep 17 00:00:00 2001 From: Max Kozlov Date: Thu, 20 Apr 2017 13:11:14 +0300 Subject: [PATCH 15/17] Massively rewritten Tests. Now it check for parameters combinations and additional switches --- Tests/PoshRSJob.Tests.ps1 | 414 ++++++++++++++++++++++++++------------ 1 file changed, 287 insertions(+), 127 deletions(-) diff --git a/Tests/PoshRSJob.Tests.ps1 b/Tests/PoshRSJob.Tests.ps1 index 9d20218..f276322 100644 --- a/Tests/PoshRSJob.Tests.ps1 +++ b/Tests/PoshRSJob.Tests.ps1 @@ -26,15 +26,91 @@ function Test-RSJob([bool]$FullPiping=$true) { $ScriptBlock = { "{0}: {1}" -f $_, [DateTime]::Now; Start-Sleep -Seconds 5 } $params = @{ Batch='throttletest'; ScriptBlock=$ScriptBlock; Throttle=5 } if ($FullPiping) { - $jobs = 1..25 | Start-RSJob @params + $jobs = 1..15 | Start-RSJob @params } else { - $jobs = 1..25 | Foreach-Object { $_ | Start-RSJob @params } + $jobs = 1..15 | Foreach-Object { $_ | Start-RSJob @params } } $jobs | Wait-RSJob | Receive-RSJob $jobs | Remove-RSJob } +$ParameterTestCases = @( + @{ + Case = 'by job object' + Mode = 0 + Param = { @{ Job = $TestJob } } + }, + @{ + Case = 'by job object positioned' + Mode = 1 + Param = { $TestJob } + }, + @{ + Case = 'by job object from pipeline (name)' + Mode = 2 + Param = { '' | Select @{n='Job'; e={$TestJob}} } + }, + @{ + Case = 'by job object from pipeline (value)' + Mode = 2 + Param = { $TestJob } + }, + + @{ + Case = 'by id' + Mode = 0 + Param = { @{ Id = ($TestJob | Select-Object -Expand id) } } + }, + @{ + Case = 'by id positioned' + Mode = 1 + Param = { $TestJob | Select-Object -Expand id } + }, + @{ + Case = 'by id from pipeline (name)' + Mode = 2 + Param = { $TestJob | Select-Object id } + }, + @{ + Case = 'by id from pipeline (value)' + Mode = 2 + Param = { $TestJob | Select-Object -Expand id } + }, + + @{ + Case = 'by name' + Mode = 0 + Param = { @{ Name = ($TestJob | Select-Object -Expand Name) } } + }, + @{ + Case = 'by name positioned' + Mode = 1 + Param = { $TestJob | Select-Object -Expand Name } + }, + @{ + Case = 'by name from pipeline (name)' + Mode = 2 + Param = { $TestJob | Select-Object Name } + }, + @{ + Case = 'by name from pipeline (value)' + Mode = 2 + Param = { $TestJob | Select-Object -Expand Name } + }, + + @{ + Case = 'by InstanceID' + Mode = 0 + Param = { @{ InstanceID = ($TestJob | Select-Object -Expand InstanceID) } } + }, + @{ + Case = 'by InstanceID from pipeline (name)' + Mode = 2 + Param = { $TestJob | Select-Object InstanceID } + } +) + Describe "PoshRSJob PS$($PSVersion)" { Context 'Strict mode' { Set-StrictMode -Version latest @@ -107,20 +183,6 @@ Describe "Start-RSJob PS$PSVersion" { } } -Describe "Stop-RSJob PS$PSVersion" { - Context 'Strict mode' { - Set-StrictMode -Version latest - It 'should stop a job' { - $Job = 1 | Start-RSJob -ScriptBlock { - While ($True) {$Null} - } - $Job | Stop-RSJob - Start-Sleep -Milliseconds 100 - $Job.State | Should be 'Stopped' - } - } -} - Describe "Get-RSJob PS$PSVersion" { Context 'Strict mode' { Set-StrictMode -Version latest @@ -128,75 +190,139 @@ Describe "Get-RSJob PS$PSVersion" { $Output = @( Get-RSJob @Verbose ) $Props = $Output[0].PSObject.Properties | Select-Object -ExpandProperty Name - $Output.count | Should be 11 + $Output.count | Should be 10 $Props -contains "Id" | Should be $True $Props -contains "State" | Should be $True $Props -contains "HasMoreData" | Should be $True } - It 'should return job details based on ID' { - $Output = @( Get-RSJob @Verbose -Id 1 ) - $Props = $Output[0].PSObject.Properties | Select-Object -ExpandProperty Name - $Output.count | Should be 1 - $Props -contains "Id" | Should be $True - $Props -contains "State" | Should be $True - $Props -contains "HasMoreData" | Should be $True + It 'should return job by state' { + 1..10 | Start-RSJob { Start-Sleep -Seconds 5; $_ } -Throttle 5 + $Jobs = @(Get-RSJob -State NotStarted) + $Jobs.Count | Should Be 5 } - It 'should return job details based on Name' { - $Output = @( Get-RSJob @Verbose -Name Job2 ) - $Props = $Output[0].PSObject.Properties | Select-Object -ExpandProperty Name - $Output.count | Should be 1 - $Props -contains "Id" | Should be $True - $Props -contains "State" | Should be $True - $Props -contains "HasMoreData" | Should be $True + It 'should return job details ' -TestCases $ParameterTestCases { + param( + $Case, + $Mode, + $Param + ) + + $TestJob = Start-RSJob -Name "TestJob $Case" -ScriptBlock { $text=$using:Case; "Working on $text" } + + switch ($Mode) { + 0 { + $Parameters = & $Param + $Output = @( Get-RSJob @Verbose @Parameters ) + } + 1 { + $Parameters = & $Param + $Output = @( Get-RSJob @Verbose $Parameters ) + } + 2 { + $Parameters = & $Param + $Output = @( $Parameters | Get-RSJob @Verbose ) + } + default { + # Fail test on invalid mode + 'Invalid mode !' | Should be 'Invalid mode !!!' + } + } + + $Output.Count | Should be 1 + $Output[0] -is 'RSJob' | Should be $true + $Output[0].Name | Should be "TestJob $Case" } } } -Describe "Remove-RSJob PS$PSVersion" { +Describe "Stop-RSJob PS$PSVersion" { Context 'Strict mode' { Set-StrictMode -Version latest - It 'should remove jobs' { - Get-RSJob @Verbose | Remove-RSJob @Verbose - $Output = @( Get-RSJob @Verbose ) - - $Output.count | Should be 0 + It 'should not stop a job' { + Stop-RSJob | Should BeNullOrEmpty } - It 'should only remove specified jobs by ID' { - $TestJobs = @( 1..5 | Start-RSJob @Verbose -ScriptBlock { "" } ) - Start-Sleep -Seconds 2 - $ThisJobId = $TestJobs[0].ID - $AllIDs = @( $TestJobs | Select-Object -ExpandProperty Id ) - Remove-RSJob @Verbose -Id $ThisJobId - $RemainingIDs = @( Get-RSJob @Verbose -Id $AllIDs | Select-Object -ExpandProperty Id ) - #We only removed one - $RemainingIDs.count -eq ($AllIDs.count - 1) | Should Be $True - #We removed the right ID - $RemainingIDs -notcontains $ThisJobId | Should Be $True + It 'should stop a job' { + $Job = 1 | Start-RSJob -ScriptBlock { + While ($True) {$Null} + } + $Job | Stop-RSJob + Start-Sleep -Milliseconds 100 + $Job.State | Should be 'Stopped' } - It 'should only remove specified jobs by Name' { - $TestJobs = @( 1..5 | Start-RSJob @Verbose -ScriptBlock { "" } ) - Start-Sleep -Seconds 2 - $ThisJobName = $TestJobs[0].Name - $AllNames = @( $TestJobs | Select-Object -ExpandProperty Name ) - Remove-RSJob @Verbose -Name $ThisJobName - $RemainingNames = @( Get-RSJob @Verbose -Name $AllNames | Select-Object -ExpandProperty Name ) - #We only removed one - $RemainingNames.count -eq ($AllNames.count - 1) | Should Be $True - #We removed the right ID - $RemainingNames -notcontains $ThisJobName | Should Be $True + It 'should stop a job ' -TestCases $ParameterTestCases { + param( + $Case, + $Mode, + $Param + ) + $TestJob = 1 | Start-RSJob -ScriptBlock { + While ($True) {$Null} + } + switch ($Mode) { + 0 { + $Parameters = & $Param + Stop-RSJob @Parameters + } + 1 { + $Parameters = & $Param + Stop-RSJob $Parameters + } + 2 { + $Parameters = & $Param + $Parameters | Stop-RSJob + } + default { + # Fail test on invalid mode + 'Invalid mode !' | Should be 'Invalid mode !!!' + } + } + Start-Sleep -Milliseconds 100 + $TestJob.State | Should be 'Stopped' } - It 'should only remove specified jobs by InputObject' { - $TestJobs = @( 1..5 | Start-RSJob @Verbose -ScriptBlock { "" }) - Start-Sleep -Seconds 2 - $ThisJob = $TestJobs[0] - $ThisJob | Remove-RSJob @Verbose - $RemainingNames = @( $TestJobs | Get-RSJob @Verbose | Select-Object -ExpandProperty Name) - #We only removed one - $RemainingNames.count -eq ($TestJobs.count - 1) | Should Be $True - #We removed the right ID - $RemainingNames -notcontains $ThisJob.Name | Should Be $True + } +} + +Describe "Wait-RSJob PS$PSVersion" { + Context 'Strict mode' { + Set-StrictMode -Version latest + + It 'should not wait a job' { + Wait-RSJob | Should BeNullOrEmpty + } + + It 'should wait for jobs ' -TestCases $ParameterTestCases { + param( + $Case, + $Mode, + $Param + ) + $StartDate = Get-Date + $TestJob = 0 | Start-RSJob @Verbose -ScriptBlock { + Start-Sleep -seconds 3 + Get-Date + } + switch ($Mode) { + 0 { + $Parameters = & $Param + Wait-RSJob @Parameters # Omitted verbose to avoid clutter + } + 1 { + $Parameters = & $Param + Wait-RSJob $Parameters # Omitted verbose to avoid clutter + } + 2 { + $Parameters = & $Param + $Parameters | Wait-RSJob # Omitted verbose to avoid clutter + } + default { + # Fail test on invalid mode + 'Invalid mode !' | Should be 'Invalid mode !!!' + } + } + $EndDate = Get-Date + ( $EndDate - $StartDate ).TotalSeconds -gt 3 | Should be $True } } } @@ -204,95 +330,129 @@ Describe "Remove-RSJob PS$PSVersion" { Describe "Receive-RSJob PS$PSVersion" { Context 'Strict mode' { Set-StrictMode -Version latest - It 'should retrieve job data' { - $TestJob = 0 | Start-RSJob @Verbose -ScriptBlock {"Working on $_"} - Start-Sleep -Seconds 1 - - $Output = @( $TestJob | Receive-RSJob @Verbose ) - $Output.Count | Should be 1 - $Output[0] | Should be "Working on 0" + It 'should not retrieve a job' { + Receive-RSJob | Should BeNullOrEmpty } - It 'should not remove the job' { - $TestJob = 0 | Start-RSJob @Verbose -ScriptBlock {""} - Start-Sleep -Seconds 1 - $TestJob | Receive-RSJob @Verbose | Out-Null - $Output = @( $TestJob | Get-RSJob @Verbose ) + It 'should retrieve job data ' -TestCases $ParameterTestCases { + param( + $Case, + $Mode, + $Param + ) + $TestJob = Get-RSJob -Name "TestJob $Case" + + switch ($Mode) { + 0 { + $Parameters = & $Param + $Output = @( Receive-RSJob @Verbose @Parameters ) + } + 1 { + $Parameters = & $Param + $Output = @( Receive-RSJob @Verbose $Parameters ) + } + 2 { + $Parameters = & $Param + $Output = @( $Parameters | Receive-RSJob @Verbose) + } + default { + # Fail test on invalid mode + 'Invalid mode !' | Should be 'Invalid mode !!!' + } + } $Output.Count | Should be 1 + $Output[0] | Should be "Working on $Case" } } } -Describe "Wait-RSJob PS$PSVersion" { +Describe "Remove-RSJob PS$PSVersion" { Context 'Strict mode' { Set-StrictMode -Version latest - It 'should wait for jobs' { - $StartDate = Get-Date - $TestJob = 0 | Start-RSJob @Verbose -ScriptBlock { - Start-Sleep -seconds 5 - Get-Date - } - $TestJob | Wait-RSJob # Omitted verbose to avoid clutter - $EndDate = Get-Date - ( $EndDate - $StartDate ).TotalSeconds -gt 5 | Should be $True + + It 'should not remove a job' { + Remove-RSJob | Should BeNullOrEmpty } - It 'should wait for jobs by Name' { - $TestJob = 1..3 | Foreach { - Start-RSJob -Name "NameTest1$_" @Verbose -ScriptBlock { - Start-Sleep -seconds 5 - Get-Date - } } - [array]$Result = Wait-RSJob -Name ($TestJob | Select -Expand Name) # Omitted verbose to avoid clutter - $Result.Count -eq $TestJob.Count | Should be $True + + It 'should only remove specified jobs ' -TestCases $ParameterTestCases { + param( + $Case, + $Mode, + $Param + ) + $TestJobs = @(Get-RSJob | Where-Object { $_.Name -match "^TestJob " }) + $TestJobs.Count -gt 0 | Should Be $True + + $TestJob = $TestJobs | Where-Object { $_.Name -eq "TestJob $Case" } + $TestJob -is 'RSJob' | Should be $true + + $AllIDs = @( $TestJobs | Select-Object -ExpandProperty Id ) + + switch ($Mode) { + 0 { + $Parameters = & $Param + Remove-RSJob @Verbose @Parameters + } + 1 { + $Parameters = & $Param + Remove-RSJob @Verbose $Parameters + } + 2 { + $Parameters = & $Param + $Parameters | Remove-RSJob @Verbose + } + default { + # Fail test on invalid mode + 'Invalid mode !' | Should be 'Invalid mode !!!' + } + } + + $RemainingIDs = @( Get-RSJob @Verbose -Id $AllIDs | Select-Object -ExpandProperty Id ) + #We only removed one + $RemainingIDs.Count -eq ($AllIDs.Count - 1) | Should Be $True + #We removed the right ID + $RemainingIDs -notcontains $TestJob.Id | Should Be $True } - It 'should wait for jobs by Name from pipeline' { - $TestJob = 1..3 | Foreach { - Start-RSJob -Name "NameTest2$_" @Verbose -ScriptBlock { - Start-Sleep -seconds 5 - Get-Date - } } - [array]$Result = $TestJob | Select Name | Wait-RSJob # Omitted verbose to avoid clutter - $Result.Count -eq $TestJob.Count | Should be $True + + It 'should not remove job' { + $TestJob = 1 | Start-RSJob -Name 'ByForce' -ScriptBlock { + While ($True) {$Null} + } + $TestJob -is 'RSJob' | Should be $true + { Remove-RSJob $TestJob -ErrorAction Stop } | Should Throw } - It 'should wait for jobs by id' { - $TestJob = 1..3 | Foreach { - Start-RSJob @Verbose -ScriptBlock { - Start-Sleep -seconds 5 - Get-Date - } } - [array]$Result = Wait-RSJob -Id ($TestJob | Select -Expand Id) # Omitted verbose to avoid clutter - $Result.Count -eq $TestJob.Count | Should be $True + It 'should remove job by force' { + $TestJob = Get-RSJob -Name 'ByForce' + $TestJob -is 'RSJob' | Should be $true + { Remove-RSJob $TestJob -Force } | Should Not Throw + $TestJob = Get-RSJob -Name 'ByForce' + $TestJob | Should BeNullOrEmpty } - It 'should wait for jobs by InstanceId' { - $TestJob = 1..3 | Foreach { - Start-RSJob @Verbose -ScriptBlock { - Start-Sleep -seconds 5 - Get-Date - } } - [array]$Result = Wait-RSJob -InstanceId ($TestJob | Select -Expand InstanceId) # Omitted verbose to avoid clutter - $Result.Count -eq $TestJob.Count | Should be $True + It 'should remove all jobs' { + Get-RSJob @Verbose | Remove-RSJob @Verbose + $Output = @( Get-RSJob @Verbose ) + + $Output.Count | Should be 0 } } } - Describe "Test RSJob Throttling" { It "Full Pipe input" { $StartDate = Get-Date Test-RSJob $true $EndDate = Get-Date - ( $EndDate - $StartDate ).TotalSeconds -gt 25 | Should be $True + ( $EndDate - $StartDate ).TotalSeconds -gt 15 | Should be $True } It "OneByOne Pipe input" { $StartDate = Get-Date Test-RSJob $false $EndDate = Get-Date - ( $EndDate - $StartDate ).TotalSeconds -gt 25 | Should be $True + ( $EndDate - $StartDate ).TotalSeconds -gt 15 | Should be $True } } - Describe "Module OnRemove Actions PS$PSVersion" { Context 'Strict mode' { Get-RSJob | Remove-RSJob From d40ed2da50651aeb1ca53111e5d42a5820c22b32 Mon Sep 17 00:00:00 2001 From: Max Kozlov Date: Thu, 20 Apr 2017 14:14:52 +0300 Subject: [PATCH 16/17] Strict-mode: Variable initialization before use --- PoshRSJob/PoshRSJob.psm1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PoshRSJob/PoshRSJob.psm1 b/PoshRSJob/PoshRSJob.psm1 index 0206397..56b84ed 100644 --- a/PoshRSJob/PoshRSJob.psm1 +++ b/PoshRSJob/PoshRSJob.psm1 @@ -191,13 +191,14 @@ $PoshRS_RunspacePoolCleanup.Runspace.SessionStateProxy.SetVariable("PoshRS_Runsp $PoshRS_RunspacePoolCleanup.Runspace.SessionStateProxy.SetVariable("ParentHost",$Host) $PoshRS_RunspacePoolCleanup.PowerShell = [PowerShell]::Create().AddScript({ #Routine to handle completed runspaces + $DisposePoshRS_RunspacePools=$False Do { #$ParentHost.ui.WriteVerboseLine("Beginning Do Statement") If ($DisposePoshRS_RunspacePools) { #Perform garbage collection [gc]::Collect() } - $DisposePoshRS_RunspacePools=$False + $DisposePoshRS_RunspacePools=$False If ($PoshRS_RunspacePools.Count -gt 0) { #$ParentHost.ui.WriteVerboseLine("$($PoshRS_RunspacePools | Out-String)") [System.Threading.Monitor]::Enter($PoshRS_RunspacePools.syncroot) From b99d94176d14f12beed48ec294ab82767905232a Mon Sep 17 00:00:00 2001 From: Max Kozlov Date: Fri, 21 Apr 2017 15:07:17 +0300 Subject: [PATCH 17/17] Try/finally around [Monitor]::Exit(), some space formatting --- PoshRSJob/PoshRSJob.psm1 | 182 ++++++++++++++------------- PoshRSJob/Public/Start-RSJob.ps1 | 204 ++++++++++++++++--------------- 2 files changed, 199 insertions(+), 187 deletions(-) diff --git a/PoshRSJob/PoshRSJob.psm1 b/PoshRSJob/PoshRSJob.psm1 index 56b84ed..99371a0 100644 --- a/PoshRSJob/PoshRSJob.psm1 +++ b/PoshRSJob/PoshRSJob.psm1 @@ -1,5 +1,5 @@ $ScriptPath = Split-Path $MyInvocation.MyCommand.Path -$PSModule = $ExecutionContext.SessionState.Module +$PSModule = $ExecutionContext.SessionState.Module $PSModuleRoot = $PSModule.ModuleBase If ($PSVersionTable['PSEdition'] -and $PSVersionTable.PSEdition -eq 'Core') { #PowerShell V4 and below will throw a parser error even if I never use the classes keyword @@ -10,7 +10,7 @@ If ($PSVersionTable['PSEdition'] -and $PSVersionTable.PSEdition -eq 'Core') { [object]$Value [string]$NewVarName } - + class RSRunspacePool{ [System.Management.Automation.Runspaces.RunspacePool]$RunspacePool [System.Management.Automation.Runspaces.RunspacePoolState]$State @@ -53,7 +53,7 @@ Else { using System.Collections.Generic; using System.Text; using System.Management.Automation; - + public class V2UsingVariable { public string Name; @@ -61,7 +61,7 @@ Else { public object Value; public string NewVarName; } - + public class RSRunspacePool { public System.Management.Automation.Runspaces.RunspacePool RunspacePool; @@ -115,60 +115,64 @@ New-Variable PoshRS_RunspacePoolCleanup -Value ([hashtable]::Synchronized(@{})) Write-Verbose "Creating routine to monitor RS jobs" $PoshRS_jobCleanup.Flag=$True $PoshRS_jobCleanup.Host = $Host -$PoshRS_jobCleanup.Runspace =[runspacefactory]::CreateRunspace() -$PoshRS_jobCleanup.Runspace.Open() -$PoshRS_jobCleanup.Runspace.SessionStateProxy.SetVariable("PoshRS_jobCleanup",$PoshRS_jobCleanup) -$PoshRS_jobCleanup.Runspace.SessionStateProxy.SetVariable("PoshRS_Jobs",$PoshRS_Jobs) +$PoshRS_jobCleanup.Runspace =[runspacefactory]::CreateRunspace() +$PoshRS_jobCleanup.Runspace.Open() +$PoshRS_jobCleanup.Runspace.SessionStateProxy.SetVariable("PoshRS_jobCleanup",$PoshRS_jobCleanup) +$PoshRS_jobCleanup.Runspace.SessionStateProxy.SetVariable("PoshRS_Jobs",$PoshRS_Jobs) $PoshRS_jobCleanup.PowerShell = [PowerShell]::Create().AddScript({ #Routine to handle completed runspaces - #$PoshRS_jobCleanup.Host.UI.WriteVerboseLine("Begin Do Loop") - Do { - [System.Threading.Monitor]::Enter($PoshRS_Jobs.syncroot) - Foreach($job in $PoshRS_Jobs) { - If ($job.Handle.isCompleted -AND (-NOT $Job.Completed)) { - #$PoshRS_jobCleanup.Host.UI.WriteVerboseLine("$($Job.Id) completed") - $Data = $null - Try { - $Data = $job.InnerJob.EndInvoke($job.Handle) - } Catch { - $CaughtErrors = $Error - #$PoshRS_jobCleanup.Host.UI.WriteVerboseLine("$($Job.Id) Caught terminating Error in job: $_") - } - #$PoshRS_jobCleanup.Host.UI.WriteVerboseLine("$($Job.Id) Checking for errors") - If ($job.InnerJob.Streams.Error -OR $CaughtErrors) { - #$PoshRS_jobCleanup.Host.UI.WriteVerboseLine("$($Job.Id) Errors Found!") - $ErrorList = New-Object System.Management.Automation.PSDataCollection[System.Management.Automation.ErrorRecord] - If ($job.InnerJob.Streams.Error) { - ForEach ($Err in $job.InnerJob.Streams.Error) { - #$PoshRS_jobCleanup.Host.UI.WriteVerboseLine("`t$($Job.Id) Adding Error") - [void]$ErrorList.Add($Err) + #$PoshRS_jobCleanup.Host.UI.WriteVerboseLine("Begin Do Loop") + Do { + [System.Threading.Monitor]::Enter($PoshRS_Jobs.syncroot) + try { + Foreach($job in $PoshRS_Jobs) { + If ($job.Handle.isCompleted -AND (-NOT $Job.Completed)) { + #$PoshRS_jobCleanup.Host.UI.WriteVerboseLine("$($Job.Id) completed") + $Data = $null + Try { + $Data = $job.InnerJob.EndInvoke($job.Handle) + } Catch { + $CaughtErrors = $Error + #$PoshRS_jobCleanup.Host.UI.WriteVerboseLine("$($Job.Id) Caught terminating Error in job: $_") + } + #$PoshRS_jobCleanup.Host.UI.WriteVerboseLine("$($Job.Id) Checking for errors") + If ($job.InnerJob.Streams.Error -OR $CaughtErrors) { + #$PoshRS_jobCleanup.Host.UI.WriteVerboseLine("$($Job.Id) Errors Found!") + $ErrorList = New-Object System.Management.Automation.PSDataCollection[System.Management.Automation.ErrorRecord] + If ($job.InnerJob.Streams.Error) { + ForEach ($Err in $job.InnerJob.Streams.Error) { + #$PoshRS_jobCleanup.Host.UI.WriteVerboseLine("`t$($Job.Id) Adding Error") + [void]$ErrorList.Add($Err) + } + } + If ($CaughtErrors) { + ForEach ($Err in $CaughtErrors) { + #$PoshRS_jobCleanup.Host.UI.WriteVerboseLine("`t$($Job.Id) Adding Error") + [void]$ErrorList.Add($Err) + } } + $job.Error = $ErrorList } - If ($CaughtErrors) { - ForEach ($Err in $CaughtErrors) { - #$PoshRS_jobCleanup.Host.UI.WriteVerboseLine("`t$($Job.Id) Adding Error") - [void]$ErrorList.Add($Err) - } + #$PoshRS_jobCleanup.Host.UI.WriteVerboseLine("$($Job.Id) Disposing job") + $job.InnerJob.dispose() + #Return type from Invoke() is a generic collection; need to verify the first index is not NULL + If (($Data.Count -gt 0) -AND (-NOT ($Data.Count -eq 1 -AND $Null -eq $Data[0]))) { + $job.output = $Data + $job.HasMoreData = $True } - $job.Error = $ErrorList + $Error.Clear() + $job.Completed = $True } - #$PoshRS_jobCleanup.Host.UI.WriteVerboseLine("$($Job.Id) Disposing job") - $job.InnerJob.dispose() - #Return type from Invoke() is a generic collection; need to verify the first index is not NULL - If (($Data.Count -gt 0) -AND (-NOT ($Data.Count -eq 1 -AND $Null -eq $Data[0]))) { - $job.output = $Data - $job.HasMoreData = $True - } - $Error.Clear() - $job.Completed = $True - } - } - [System.Threading.Monitor]::Exit($PoshRS_Jobs.syncroot) - Start-Sleep -Milliseconds 100 + } + } + finally { + [System.Threading.Monitor]::Exit($PoshRS_Jobs.syncroot) + } + Start-Sleep -Milliseconds 100 } while ($PoshRS_jobCleanup.Flag) }) $PoshRS_jobCleanup.PowerShell.Runspace = $PoshRS_jobCleanup.Runspace -$PoshRS_jobCleanup.Handle = $PoshRS_jobCleanup.PowerShell.BeginInvoke() +$PoshRS_jobCleanup.Handle = $PoshRS_jobCleanup.PowerShell.BeginInvoke() Write-Verbose "Creating routine to monitor Runspace Pools" $PoshRS_RunspacePoolCleanup.Flag=$True @@ -176,7 +180,7 @@ $PoshRS_RunspacePoolCleanup.Host=$Host #2 minute timeout for unused runspace pools $PoshRS_RunspacePoolCleanup.Timeout = [timespan]::FromMinutes(2).Ticks $InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault() - + #Create Type Collection so the object will work properly $Types = Get-ChildItem "$($PSScriptRoot)\TypeData" -Filter *Types* | Select-Object -ExpandProperty Fullname ForEach ($Type in $Types) { @@ -184,62 +188,66 @@ ForEach ($Type in $Types) { $InitialSessionState.Types.Add($TypeConfigEntry) } $PoshRS_RunspacePoolCleanup.Runspace =[runspacefactory]::CreateRunspace($InitialSessionState) - -$PoshRS_RunspacePoolCleanup.Runspace.Open() -$PoshRS_RunspacePoolCleanup.Runspace.SessionStateProxy.SetVariable("PoshRS_RunspacePoolCleanup",$PoshRS_RunspacePoolCleanup) -$PoshRS_RunspacePoolCleanup.Runspace.SessionStateProxy.SetVariable("PoshRS_RunspacePools",$PoshRS_RunspacePools) -$PoshRS_RunspacePoolCleanup.Runspace.SessionStateProxy.SetVariable("ParentHost",$Host) + +$PoshRS_RunspacePoolCleanup.Runspace.Open() +$PoshRS_RunspacePoolCleanup.Runspace.SessionStateProxy.SetVariable("PoshRS_RunspacePoolCleanup",$PoshRS_RunspacePoolCleanup) +$PoshRS_RunspacePoolCleanup.Runspace.SessionStateProxy.SetVariable("PoshRS_RunspacePools",$PoshRS_RunspacePools) +$PoshRS_RunspacePoolCleanup.Runspace.SessionStateProxy.SetVariable("ParentHost",$Host) $PoshRS_RunspacePoolCleanup.PowerShell = [PowerShell]::Create().AddScript({ #Routine to handle completed runspaces $DisposePoshRS_RunspacePools=$False - Do { + Do { #$ParentHost.ui.WriteVerboseLine("Beginning Do Statement") If ($DisposePoshRS_RunspacePools) { #Perform garbage collection [gc]::Collect() } $DisposePoshRS_RunspacePools=$False - If ($PoshRS_RunspacePools.Count -gt 0) { - #$ParentHost.ui.WriteVerboseLine("$($PoshRS_RunspacePools | Out-String)") - [System.Threading.Monitor]::Enter($PoshRS_RunspacePools.syncroot) - Foreach($RunspacePool in $PoshRS_RunspacePools) { - #$ParentHost.ui.WriteVerboseLine("RunspacePool <$($RunspacePool.RunspacePoolID)> | MaxJobs: $($RunspacePool.MaxJobs) | AvailJobs: $($RunspacePool.AvailableJobs)") - If (($RunspacePool.AvailableJobs -eq $RunspacePool.MaxJobs) -AND $PoshRS_RunspacePools.LastActivity.Ticks -ne 0) { - If ((Get-Date).Ticks - $RunspacePool.LastActivity.Ticks -gt $PoshRS_RunspacePoolCleanup.Timeout) { - #Dispose of runspace pool - $RunspacePool.RunspacePool.Close() - $RunspacePool.RunspacePool.Dispose() - $RunspacePool.CanDispose = $True - $DisposePoshRS_RunspacePools=$True + If ($PoshRS_RunspacePools.Count -gt 0) { + #$ParentHost.ui.WriteVerboseLine("$($PoshRS_RunspacePools | Out-String)") + [System.Threading.Monitor]::Enter($PoshRS_RunspacePools.syncroot) + try { + Foreach($RunspacePool in $PoshRS_RunspacePools) { + #$ParentHost.ui.WriteVerboseLine("RunspacePool <$($RunspacePool.RunspacePoolID)> | MaxJobs: $($RunspacePool.MaxJobs) | AvailJobs: $($RunspacePool.AvailableJobs)") + If (($RunspacePool.AvailableJobs -eq $RunspacePool.MaxJobs) -AND $PoshRS_RunspacePools.LastActivity.Ticks -ne 0) { + If ((Get-Date).Ticks - $RunspacePool.LastActivity.Ticks -gt $PoshRS_RunspacePoolCleanup.Timeout) { + #Dispose of runspace pool + $RunspacePool.RunspacePool.Close() + $RunspacePool.RunspacePool.Dispose() + $RunspacePool.CanDispose = $True + $DisposePoshRS_RunspacePools=$True + } + } Else { + $RunspacePool.LastActivity = (Get-Date) } - } Else { - $RunspacePool.LastActivity = (Get-Date) - } - } - #Remove runspace pools - If ($DisposePoshRS_RunspacePools) { - $TempCollection = $PoshRS_RunspacePools.Clone() - $TempCollection | Where-Object { - $_.CanDispose - } | ForEach-Object { - #$ParentHost.ui.WriteVerboseLine("Removing runspacepool <$($_.RunspacePoolID)>") - [void]$PoshRS_RunspacePools.Remove($_) } - #Not setting this to silentlycontinue seems to cause another runspace to be created if an error occurs - Remove-Variable TempCollection -ErrorAction SilentlyContinue + #Remove runspace pools + If ($DisposePoshRS_RunspacePools) { + $TempCollection = $PoshRS_RunspacePools.Clone() + $TempCollection | Where-Object { + $_.CanDispose + } | ForEach-Object { + #$ParentHost.ui.WriteVerboseLine("Removing runspacepool <$($_.RunspacePoolID)>") + [void]$PoshRS_RunspacePools.Remove($_) + } + #Not setting this to silentlycontinue seems to cause another runspace to be created if an error occurs + Remove-Variable TempCollection -ErrorAction SilentlyContinue + } + } + finally { + [System.Threading.Monitor]::Exit($PoshRS_RunspacePools.syncroot) } - [System.Threading.Monitor]::Exit($PoshRS_RunspacePools.syncroot) } - #$ParentHost.ui.WriteVerboseLine("Sleeping") + #$ParentHost.ui.WriteVerboseLine("Sleeping") If ($DisposePoshRS_RunspacePools) { #Perform garbage collection [gc]::Collect() } - Start-Sleep -Milliseconds 5000 + Start-Sleep -Milliseconds 5000 } while ($PoshRS_RunspacePoolCleanup.Flag) }) $PoshRS_RunspacePoolCleanup.PowerShell.Runspace = $PoshRS_RunspacePoolCleanup.Runspace -$PoshRS_RunspacePoolCleanup.Handle = $PoshRS_RunspacePoolCleanup.PowerShell.BeginInvoke() +$PoshRS_RunspacePoolCleanup.Handle = $PoshRS_RunspacePoolCleanup.PowerShell.BeginInvoke() #endregion Cleanup Routine #region Load Public Functions @@ -293,7 +301,7 @@ $PoshRS_OnRemoveScript = { #Let sit for a second to make sure it has had time to stop Start-Sleep -Seconds 1 $PoshRS_jobCleanup.PowerShell.EndInvoke($PoshRS_jobCleanup.Handle) - $PoshRS_jobCleanup.PowerShell.Dispose() + $PoshRS_jobCleanup.PowerShell.Dispose() $PoshRS_RunspacePoolCleanup.PowerShell.EndInvoke($PoshRS_RunspacePoolCleanup.Handle) $PoshRS_RunspacePoolCleanup.PowerShell.Dispose() Remove-Variable PoshRS_JobId -Scope Global -Force diff --git a/PoshRSJob/Public/Start-RSJob.ps1 b/PoshRSJob/Public/Start-RSJob.ps1 index a8a79ad..9775ac5 100644 --- a/PoshRSJob/Public/Start-RSJob.ps1 +++ b/PoshRSJob/Public/Start-RSJob.ps1 @@ -44,7 +44,7 @@ Function Start-RSJob { .NOTES Name: Start-RSJob - Author: Boe Prox + Author: Boe Prox .EXAMPLE Get-ChildItem -Directory | Start-RSjob -Name {$_.Name} -ScriptBlock { @@ -56,8 +56,8 @@ Function Start-RSJob { Name = $Directory.Name SizeMB = ([math]::round(($Sum/1MB),2)) } - } - + } + Id Name State HasMoreData HasErrors Command -- ---- ----- ----------- --------- ------- 13 Contacts Running False False ... @@ -92,8 +92,8 @@ Function Start-RSJob { Description ----------- - Starts a background runspace job that looks at the total size of each folder. Using Get-RSJob | Recieve-RSJob shows - the results when the State is Completed. + Starts a background runspace job that looks at the total size of each folder. Using Get-RSJob | Recieve-RSJob shows + the results when the State is Completed. .EXAMPLE $Test = 'test' @@ -104,7 +104,7 @@ Function Start-RSJob { Test=$Using:Test Something=$Using:Something } - } + } Id Name State HasMoreData HasErrors Command -- ---- ----- ----------- --------- ------- @@ -113,7 +113,7 @@ Function Start-RSJob { 78 3 Running False False ... 79 4 Completed False False ... 80 5 Completed False False ... - + Get-RSjob | Receive-RSJob Result Test Something @@ -123,7 +123,7 @@ Function Start-RSJob { 6 test {1, 2, 3, 4...} 8 test {1, 2, 3, 4...} 10 test {1, 2, 3, 4...} - + Description ----------- Shows an example of the $Using: variable being used in the scriptblock. @@ -145,7 +145,7 @@ Function Start-RSJob { } 1..5|Start-RSJob $ScriptBlock -ArgumentList $test, $anothertest - + Description ----------- Shows an example of the $Using: variable being used in the scriptblock as well as $_ and multiple -ArgumentList parameters. @@ -177,11 +177,11 @@ Function Start-RSJob { [parameter()] [string[]]$FunctionsToLoad ) - Begin { + Begin { If ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' - } - Write-Debug "[BEGIN]" + } + Write-Debug "[BEGIN]" If ($PSBoundParameters.ContainsKey('Verbose')) { Write-Verbose "Displaying PSBoundParameters" $PSBoundParameters.GetEnumerator() | ForEach-Object { @@ -195,11 +195,11 @@ Function Start-RSJob { Else { $JobName = [scriptblock]::Create( ($Name -replace '\$_','$Item')) } - } + } Else { Write-Verbose "Creating default Job Name" $JobName = [scriptblock]::Create('Write-Output Job$($Id)') - } + } $InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault() If ($PSBoundParameters['ModulesToImport']) { [void]$InitialSessionState.ImportPSModule($ModulesToImport) @@ -217,8 +217,8 @@ Function Start-RSJob { $Definition = Get-Content Function:\$Function -ErrorAction Stop Write-Debug "Definition: $($Definition)" $SessionStateFunction = New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList $Function, $Definition - $InitialSessionState.Commands.Add($SessionStateFunction) - } + $InitialSessionState.Commands.Add($SessionStateFunction) + } Catch { Write-Warning "$($Function): $($_.Exception.Message)" } @@ -228,13 +228,13 @@ Function Start-RSJob { $AliasEntry = New-Object System.Management.Automation.Runspaces.SessionStateAliasEntry -ArgumentList $Alias.Name,$Alias.Definition $InitialSessionState.Commands.Add($AliasEntry) } - } + } } If ($PSBoundParameters.ContainsKey('ArgumentList')) { Write-Debug "$(@($ArgumentList).count) argument/s passed via -ArgumentList" If (@($ArgumentList).count -le 1) { $SingleArgument = $True - } + } Else { $SingleArgument = $False } @@ -244,18 +244,18 @@ Function Start-RSJob { } $Script:List = New-Object System.Collections.ArrayList - If ($PSBoundParameters.ContainsKey('InputObject')) { - [void]$list.AddRange(@($InputObject)) + If ($PSBoundParameters.ContainsKey('InputObject')) { + [void]$list.AddRange(@($InputObject)) $IsPipeline = $True $IgnoreProcess = $True - } + } ElseIf ($PSCmdlet.SessionState.PSVariable.Get('_') -AND $PSVersionTable.PSVersion.Major -ne2) { Write-Debug '$_ found from ForEach loop' $IsPipeline = $True $IgnoreProcess = $False #[void]$list.AddRange(@($PSCmdlet.SessionState.PSVariable.Get('_') | Select-Object -ExpandProperty Value)) Try { - $InputObject = $PSBoundParameters['InputObject'] = @($PSCmdlet.SessionState.PSVariable.Get('_') | + $InputObject = $PSBoundParameters['InputObject'] = @($PSCmdlet.SessionState.PSVariable.Get('_') | Select-Object -ExpandProperty Value) } Catch { @@ -268,10 +268,10 @@ Function Start-RSJob { } } Process { - Write-Debug "[PROCESS]" + Write-Debug "[PROCESS]" If ($IsPipeline -AND $PSBoundParameters.ContainsKey('InputObject') -AND -NOT $IgnoreProcess) { [void]$List.Add($InputObject) - } + } ElseIf ($IgnoreProcess) { $IsPipeline = $True } @@ -279,19 +279,19 @@ Function Start-RSJob { $IsPipeline = $True } } - End { - Write-Debug "[END]" + End { + Write-Debug "[END]" $SBParamCount = @(GetParamVariable -ScriptBlock $ScriptBlock).Count $ArgumentCount = If (-NOT $ArgumentList -or ($SingleArgument -eq 0)) { # Empty array, or, 0 count 0 - } + } Else { @($ArgumentList).count } If ($PSBoundParameters.ContainsKey('InputObject')) { If ($ArgumentCount -gt 0) { $SBParamCount++ - } + } Else { $SBParamCount-- } @@ -300,7 +300,7 @@ Function Start-RSJob { If ($ArgumentCount -ne $SBParamCount -AND $IsPipeline) { Write-Verbose 'Will use $_ in Param() Block' $Script:Add_ = $True - } + } Else { $Script:Add_ = $False } @@ -312,7 +312,7 @@ Function Start-RSJob { Switch ($PSVersionTable.PSVersion.Major) { 2 { Write-Verbose "Using PSParser with PowerShell V2" - $UsingVariables = @(GetUsingVariablesV2 -ScriptBlock $ScriptBlock) + $UsingVariables = @(GetUsingVariablesV2 -ScriptBlock $ScriptBlock) Write-Verbose "Using Count: $($UsingVariables.count)" Write-Verbose "$($UsingVariables|Out-String)" Write-Verbose "CommandOrigin: $($MyInvocation.CommandOrigin)" @@ -322,7 +322,7 @@ Function Start-RSJob { Try { If ($MyInvocation.CommandOrigin -eq 'Runspace') { $Value = (Get-Variable -Name $Name).Value - } + } Else { $Value = $PSCmdlet.SessionState.PSVariable.Get($Name).Value If ([string]::IsNullOrEmpty($Value)) { @@ -335,35 +335,35 @@ Function Start-RSJob { Value = $Value NewVarName = ('__using_{0}') -f $Name } - } + } Catch { - Throw "Start-RSJob : The value of the using variable '$($Var.SubExpression.Extent.Text)' cannot be retrieved because it has not been set in the local session." + Throw "Start-RSJob : The value of the using variable '$($Var.SubExpression.Extent.Text)' cannot be retrieved because it has not been set in the local session." } }) Write-Verbose ("Found {0} `$Using: variables!" -f $UsingVariableValues.count) } If ($UsingVariables.count -gt 0 -OR $Script:Add_) { - $NewScriptBlock = ConvertScriptBlockV2 $ScriptBlock -UsingVariable $UsingVariables -UsingVariableValue $UsingVariableValues - } + $NewScriptBlock = ConvertScriptBlockV2 $ScriptBlock -UsingVariable $UsingVariables -UsingVariableValue $UsingVariableValues + } Else { $NewScriptBlock = $ScriptBlock - } + } } Default { Write-Debug "Using AST with PowerShell V3+" $UsingVariables = @(GetUsingVariables $ScriptBlock | Group-Object SubExpression | ForEach-Object { $_.Group | Select-Object -First 1 - }) - #region Get Variable Values + }) + #region Get Variable Values If ($UsingVariables.count -gt 0) { - $UsingVar = $UsingVariables | Group-Object SubExpression | ForEach-Object {$_.Group | Select-Object -First 1} - Write-Debug "CommandOrigin: $($MyInvocation.CommandOrigin)" + $UsingVar = $UsingVariables | Group-Object SubExpression | ForEach-Object {$_.Group | Select-Object -First 1} + Write-Debug "CommandOrigin: $($MyInvocation.CommandOrigin)" $UsingVariableValues = @(ForEach ($Var in $UsingVar) { Try { If ($MyInvocation.CommandOrigin -eq 'Runspace') { $Value = Get-Variable -Name $Var.SubExpression.VariablePath.UserPath - } + } Else { $Value = ($PSCmdlet.SessionState.PSVariable.Get($Var.SubExpression.VariablePath.UserPath)) If ([string]::IsNullOrEmpty($Value)) { @@ -376,7 +376,7 @@ Function Start-RSJob { NewName = ('$__using_{0}' -f $Var.SubExpression.VariablePath.UserPath) NewVarName = ('__using_{0}' -f $Var.SubExpression.VariablePath.UserPath) } - } + } Catch { Throw "Start-RSJob : The value of the using variable '$($Var.SubExpression.Extent.Text)' cannot be retrieved because it has not been set in the local session." } @@ -385,8 +385,8 @@ Function Start-RSJob { Write-Verbose ("Found {0} `$Using: variables!" -f $UsingVariableValues.count) } If ($UsingVariables.count -gt 0 -OR $Script:Add_) { - $NewScriptBlock = ConvertScript $ScriptBlock - } + $NewScriptBlock = ConvertScript $ScriptBlock + } Else { $NewScriptBlock = $ScriptBlock } @@ -397,49 +397,53 @@ Function Start-RSJob { Write-Debug "ScriptBlock: $($NewScriptBlock)" - #region RunspacePool Creation - - [System.Threading.Monitor]::Enter($PoshRS_RunspacePools.syncroot) - $__RSPObject = $PoshRS_RunspacePools | Where-Object { - $_.RunspacePoolID -eq $Batch - } - If ($__RSPObject) { - Write-Verbose "Using current runspacepool <$($__RSPObject.RunspacePoolID)>" - $RunspacePoolID = $__RSPObject.RunspacePoolID - $RSPObject = $__RSPObject - $RSPObject.LastActivity = Get-Date - } - Else { - Write-Verbose "Creating new runspacepool <$Batch>" - $RunspacePoolID = $Batch - $RunspacePool = [runspacefactory]::CreateRunspacePool($InitialSessionState) - If ($RunspacePool.psobject.Properties["ApartmentState"]) { - #ApartmentState doesn't exist in Nano Server - $RunspacePool.ApartmentState = 'STA' - } - [void]$RunspacePool.SetMaxRunspaces($Throttle) - If ($PSVersionTable.PSVersion.Major -gt 2) { - $RunspacePool.CleanupInterval = [timespan]::FromMinutes(2) + #region RunspacePool Creation + + [System.Threading.Monitor]::Enter($PoshRS_RunspacePools.syncroot) + try { + $__RSPObject = $PoshRS_RunspacePools | Where-Object { + $_.RunspacePoolID -eq $Batch } - $RunspacePool.Open() - $RSPObject = New-Object RSRunspacePool -Property @{ - RunspacePool = $RunspacePool - MaxJobs = $RunspacePool.GetMaxRunspaces() - RunspacePoolID = $RunspacePoolID - LastActivity = Get-Date + If ($__RSPObject) { + Write-Verbose "Using current runspacepool <$($__RSPObject.RunspacePoolID)>" + $RunspacePoolID = $__RSPObject.RunspacePoolID + $RSPObject = $__RSPObject + $RSPObject.LastActivity = Get-Date } - - #[System.Threading.Monitor]::Enter($PoshRS_RunspacePools.syncroot) #Temp add - [void]$PoshRS_RunspacePools.Add($RSPObject) + Else { + Write-Verbose "Creating new runspacepool <$Batch>" + $RunspacePoolID = $Batch + $RunspacePool = [runspacefactory]::CreateRunspacePool($InitialSessionState) + If ($RunspacePool.psobject.Properties["ApartmentState"]) { + #ApartmentState doesn't exist in Nano Server + $RunspacePool.ApartmentState = 'STA' + } + [void]$RunspacePool.SetMaxRunspaces($Throttle) + If ($PSVersionTable.PSVersion.Major -gt 2) { + $RunspacePool.CleanupInterval = [timespan]::FromMinutes(2) + } + $RunspacePool.Open() + $RSPObject = New-Object RSRunspacePool -Property @{ + RunspacePool = $RunspacePool + MaxJobs = $RunspacePool.GetMaxRunspaces() + RunspacePoolID = $RunspacePoolID + LastActivity = Get-Date + } + + #[System.Threading.Monitor]::Enter($PoshRS_RunspacePools.syncroot) #Temp add + [void]$PoshRS_RunspacePools.Add($RSPObject) + } + } + finally { + [System.Threading.Monitor]::Exit($PoshRS_RunspacePools.syncroot) } - [System.Threading.Monitor]::Exit($PoshRS_RunspacePools.syncroot) #endregion RunspacePool Creation If ($List.Count -gt 0) { Write-Debug "InputObject" Write-Debug "ListCount: $($list.count)" ForEach ($Item in $list) { - $ID = Increment + $ID = Increment Write-Verbose "Using $($Item) as pipeline variable" $PowerShell = [powershell]::Create().AddScript($NewScriptBlock) $PowerShell.RunspacePool = $RSPObject.RunspacePool @@ -453,16 +457,16 @@ Function Start-RSJob { } Write-Verbose "Checking for ArgumentList" If ($PSBoundParameters.ContainsKey('ArgumentList')) { - If ($SingleArgument) { + If ($SingleArgument) { ,$ArgumentList | ForEach-Object { Write-Verbose "Adding Argument: $($_) <$($_.GetType().Fullname)>" - [void]$PowerShell.AddArgument($_) + [void]$PowerShell.AddArgument($_) } - } + } Else { ForEach ($Argument in $ArgumentList) { Write-Verbose "Adding Argument: $($Argument) <$($Argument.GetType().Fullname)>" - [void]$PowerShell.AddArgument($Argument) + [void]$PowerShell.AddArgument($Argument) } } } @@ -471,7 +475,7 @@ Function Start-RSJob { Write-Verbose "Determining Job Name" $_JobName = If ($PSVersionTable.PSVersion.Major -eq 2) { $JobName.Invoke() - } + } Else { $JobName.InvokeReturnAsIs() } @@ -479,7 +483,7 @@ Function Start-RSJob { Name = $_JobName InputObject = $Item InstanceID = [guid]::NewGuid().ToString() - ID = $ID + ID = $ID Handle = $Handle InnerJob = $PowerShell Runspace = $PowerShell.Runspace @@ -488,19 +492,19 @@ Function Start-RSJob { RunspacePoolID = $RunSpacePoolID Batch = $Batch } - + $RSPObject.LastActivity = Get-Date Write-Verbose "Adding RSJob to Jobs queue" - [System.Threading.Monitor]::Enter($PoshRS_Jobs.syncroot) + [System.Threading.Monitor]::Enter($PoshRS_Jobs.syncroot) [void]$PoshRS_Jobs.Add($Object) - [System.Threading.Monitor]::Exit($PoshRS_Jobs.syncroot) + [System.Threading.Monitor]::Exit($PoshRS_Jobs.syncroot) Write-Verbose "Display RSJob" - $Object + $Object } - } + } Else { Write-Debug "No InputObject" - $ID = Increment + $ID = Increment $PowerShell = [powershell]::Create().AddScript($NewScriptBlock) $PowerShell.RunspacePool = $RSPObject.RunspacePool If ($UsingVariableValues) { @@ -513,20 +517,20 @@ Function Start-RSJob { If ($SingleArgument) { ,$ArgumentList | ForEach-Object { Write-Verbose "Adding Argument: $($_) <$($_.GetType().Fullname)>" - [void]$PowerShell.AddArgument($_) + [void]$PowerShell.AddArgument($_) } - } + } Else { ForEach ($Argument in $ArgumentList) { Write-Verbose "Adding Argument: $($Argument) <$($Argument.GetType().Fullname)>" - [void]$PowerShell.AddArgument($Argument) + [void]$PowerShell.AddArgument($Argument) } } } $Handle = $PowerShell.BeginInvoke() $_JobName = If ($PSVersionTable.PSVersion.Major -eq 2) { $JobName.Invoke() - } + } Else { $JobName.InvokeReturnAsIs() } @@ -534,7 +538,7 @@ Function Start-RSJob { Name = $_JobName InputObject = $null InstanceID = [guid]::NewGuid().ToString() - ID = $ID + ID = $ID Handle = $Handle InnerJob = $PowerShell Runspace = $PowerShell.Runspace @@ -543,12 +547,12 @@ Function Start-RSJob { RunspacePoolID = $RunSpacePoolID Batch = $Batch } - + $RSPObject.LastActivity = Get-Date - [System.Threading.Monitor]::Enter($PoshRS_Jobs.syncroot) + [System.Threading.Monitor]::Enter($PoshRS_Jobs.syncroot) [void]$PoshRS_Jobs.Add($Object) - [System.Threading.Monitor]::Exit($PoshRS_Jobs.syncroot) - $Object - } + [System.Threading.Monitor]::Exit($PoshRS_Jobs.syncroot) + $Object + } } }