RamblingCookieMonster / Invoke-Parallel

Speed up PowerShell with simplified multithreading
MIT License
384 stars 88 forks source link

Weird error running Invoke-Parallel #62

Open MarkKharitonov opened 5 years ago

MarkKharitonov commented 5 years ago

Please, observe the following script:

param([switch]$Cleanup, [Switch]$MockMutex, $N = 1)

function Cleanup()
{
    1..$N | ForEach-Object {
        Unregister-PSRepository "r$_" -ErrorAction SilentlyContinue
    }
    Remove-Item $WorkDir -ErrorAction SilentlyContinue -Recurse
}

$WorkDir = "$env:TEMP\fb22c60e-a0c5-48b3-953a-0b580c6a2f5e"
if ($Cleanup)
{
    Cleanup
}
else
{
    if ($MockMutex)
    {
        class MutexMock
        {
            WaitOne() { }
            ReleaseMutex() { }
        }
        $mtx = [MutexMock]::New()
    }
    else
    {
        $mtx = [Threading.Mutex]::New($false)
    }

    1..$N | Invoke-Parallel {
        $RepositoryName = "r$_"
        $WorkDir = $Using:WorkDir

        $null = mkdir "$WorkDir\$RepositoryName" -Force -ErrorAction SilentlyContinue

        $null = $parameter.WaitOne()
        try
        {
            if (!(Get-PSRepository -Name $RepositoryName -ErrorAction SilentlyContinue))
            {
                Write-Host "[$([AppDomain]::GetCurrentThreadId())] BEGIN[$RepositoryName]"
                Register-PSRepository $RepositoryName `
                    -SourceLocation "$WorkDir\$RepositoryName" `
                    -PublishLocation "$WorkDir\$RepositoryName" `
                    -InstallationPolicy Trusted
                Write-Host "[$([AppDomain]::GetCurrentThreadId())]      [$RepositoryName]END"
            }
        }
        finally
        {
            $parameter.ReleaseMutex()
        }
    } -Parameter $mtx
    Get-PSRepository | Where-Object { $_.Name.StartsWith('r') }
    Cleanup
}

It just tries to run Register-PSRepository in parallel the given number of times. The default is to use a mutex to actually serialize the calls to the functions Get-PSRepository and Register-PSRepository (thus killing the parallelism, of course, but this is a different story). Passing -MockMutex switch replaces a real mutex object with a mock.

Strange things happen when calling with the mock and N greater than 1:

PS C:\> .\1.ps1 -MockMutex -N 2
Get-RunspaceData : A parameter cannot be found that matches parameter name 'Provider'.
At C:\misc\Invoke-Parallel.ps1:591 char:13
+             Get-RunspaceData -wait
+             ~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Write-Error], ParameterBindingException
    + FullyQualifiedErrorId : NamedParameterNotFound,Get-RunspaceData

[37616] BEGIN[r1]
[37616]      [r1]END

Name                      InstallationPolicy   SourceLocation
----                      ------------------   --------------
r1                        Trusted              C:\Users\MKHARI~1\AppData\Local\Temp\fb22c60e-a0c5-48b3-953a-0b580c6a2f5e\r1

PS C:\>

All works fine when N = 1 or when called with a real mutex object.

The only explanation that I see is that the Get/Register-PSRepository functions are not multi-thread safe and calling them in parallel screws things up in such a way that it affects Invoke-Parallel somehow.