pf-async.ps1
# Requires -Module pf-basic function Invoke-RunSpace { PAram([ScriptBlock]$script, [HashTable]$Parameters) try { $Runspace = [runspacefactory]::CreateRunspace() $ps = [powershell]::create() $ps.Runspace = $Runspace $Runspace.Open() $ps.AddScript( $script ) if ($Parameters) { $ps.AddParameters($Parameters) } $async = $ps.BeginInvoke() $async.IsCompleted $result = $ps.EndInvoke($async) return $result } finally { Invoke-Dispose ([ref] $Runspace) Invoke-Dispose ([ref] $ps) } } function Invoke-RunSpace:::Example { $script = { PARAM($dest) 'START' ping $dest 'END' } Invoke-RunSpace -script $script -Parameters @{dest = $env:COMPUTERNAME} } function Invoke-Script_InMutexBlock( [ScriptBlock]$script, $ArgumentList, [string]$name, [TimeSpan]$TimeOut) { if (-not $name) { #If no name is provided for the mutex, one is generated for the script # based on where this function was called $name = Get-ScriptLine -level 2 #If this follows a file format it will try to create a lock on a file $name = $name | Update-Path_ReplaceFileSpecialChars } $mtx = New-Object System.Threading.Mutex($false, $name) try { if ( $mtx.WaitOne($TimeOut) ) { $result = Invoke-Command -ScriptBlock $script -ArgumentList $ArgumentList return $result } else { Write-Warning "Mutex '$name' not obtained " } } finally { $mtx.ReleaseMutex() Invoke-Dispose ([ref] $mtx) } } function Invoke-Script_InMutexBlock:::Test { $initScript = New-ScriptBlock "import-module Common -DisableNameChecking" $AssertFolder = "$env:temp\PSAssert" New-Folder_EnsureExists $AssertFolder $timeStamp = [DateTime]::Now.Ticks $testFile = "$AssertFolder\Invoke-Script_InMutexBlock_$timeStamp.test" # Every execution use a different GUID the test passes if no other instance change the GUID stored in the file [ScriptBlock]$script = { param ( $testFile ) Write-Host "Start $PID $testFile" $mutexName = $testFile | Update-Path_ReplaceFileSpecialChars Invoke-Script_InMutexBlock -name $mutexName -TimeOut '00:01:00' -script { $expected = [Guid]::NewGuid().ToString() Write-Host "Mutex Start $PID $testFile $expected" Set-Content $testFile $expected Start-Sleep -Milliseconds 1000 $actual = ( Get-Content $testFile -Raw ).Trim() Write-Host "Mutex End $PID $testFile $expected" return ( $actual -eq $expected ) } } $jobs = @() try { # Test Script alone Invoke-Command -ScriptBlock $script -ArgumentList $testFile | assert -eq $true # Use concurrent Jobs $jobs = (1..10) | ForEach-Object { start-job -ScriptBlock $script -InitializationScript $initScript -ArgumentList $testFile } $jobs | Wait-Job -Timeout 200 | Out-Null $results = $jobs | Receive-Job Write-Debug $results } finally { $jobs | Remove-Job if (Test-Path $testFile) { Remove-Item $testFile } } } function wait-until { param ( [scriptblock]$condition, [timespan]$timeout = [TimeSpan]::FromSeconds(30), [timespan]$testInterval = [TimeSpan]::FromSeconds(1), [string]$Activity, [scriptblock]$canRetry ) $deadline = [DateTime]::Now + $timeout $tics = 0 $lineWidth = 60 #$Host.UI.RawUI.WindowSize.Width if (-not $Activity) { $Activity = $condition.ToString() } write-host "wait-until $Activity" $startedAt = [DateTime]::Now while ($true) { if (-not $canRetry ) { $result = & $condition } else { try { $result = & $condition } catch { $retry = Invoke-Command -ScriptBlock $canRetry -ArgumentList $_ if (-not $retry) { throw $_ } else { Write-Warning "Retry $($_.ToString())" } } } if ( $result ) { Write-Progress -Activity "$Activity" -Completed return $result } $remaning = $deadline - [DateTime]::Now if ( $remaning.TotalSeconds -lt 0 ) { throw "Timeout. wait-until '$condition' " } Write-Progress -Activity "$Activity" -SecondsRemaining $remaning.TotalSeconds write-host '.' -NoNewline $tics++ if ( $tics % $lineWidth -eq 0 ) { $duration = [DateTime]::Now - $startedAt write-host $duration.ToString() } Start-Sleep -Milliseconds $testInterval.TotalMilliseconds } } function wait-until:::Test { $d = [DateTime]::Now.AddSeconds(10); wait-until -condition { [DateTime]::Now -gt $d } -timeout ([TimeSpan]'00:00:20') | assert $true { $d = [DateTime]::Now.AddSeconds(4); wait-until -condition { [DateTime]::Now -gt $d } -timeout ([TimeSpan]'00:00:02') } | assert -throw 'Timeout' } |