Read output streams before calling WaitForExit() to avoid a deadlock for packages with many dependencies.

For packages with many dependencies the output stream from calling NuGet.exe in functions/Run-NuGet can be filled and cause a deadlock [as described on MSDN][1].

Moving the call to WaitForExit() to after the output streams have been read solves this problem.

[1]: http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.redirectstandardoutput.aspx
I commented my code a bit and may have pushed it. Unregister Event seems to
be the secret sauce in getting Register Object Event to actually wait for
exit (making it reliable). That was quite a bit of testing going in there
to get that working properly along with a bunch of WTF, powershell...


On Wednesday, June 18, 2014, Jakub Berezanski <notifications@github.com>
wrote:

> In src/functions/Run-NuGet.ps1:
>
> >
> >    $nugetOutput = $process.StandardOutput.ReadToEnd()
> >    $errors = $process.StandardError.ReadToEnd()
> >
> > +  $process.WaitForExit()
> > +
>
> OK, I've tested a bunch of approaches. I've found a few working ones,
> apart from Register-ObjectEvent.
> My test code and test results (in comments, lines 143-186):
> https://gist.github.com/jberezanski/f329ec1ccdf9c7dfb5ae
>
> The Register-ObjectEvent approach is not only one of the slowest, but
> also, surprisingly, unreliable! Due to the async nature of PowerShell event
> dispatch, there is no guarantee that all output events will be delivered by
> the time the child process ends. Without the Start-Sleep after
> WaitForExit(), I was getting random test failures (empty error output).
>
> The fastest reliable approach involves using a Task obtained from
> $process.StandardError.ReadToEndAsync(), but this method is only available
> on .NET 4.0, so requires PowerShell 3 or better. This approach also results
> in cleanest code.
>
> The second-best approach uses Delegate.BeginInvoke() to call ReadToEnd()
> asynchronously. I could not find a suitable delegate signature in pure .NET
> 2.0 and had to use Func, available in .NET 3.5 upwards. This approach works
> in PowerShell 2.0 if .NET 3.5 is installed.
>
> The fastest approach supporting pure .NET 2.0 is the one where a dynamic
> method is generated from MSIL.
>
> (Note that I was comparing the approaches based on first invocation time,
> where the difference is most noticeable. Second and later invocations are
> equally fast in all approaches except Register-ObjectEvent.)
>
> —
> Reply to this email directly or view it on GitHub
> <https://github.com/chocolatey/chocolatey/pull/488/files#r13947935>.
>


-- 

Rob
"Be passionate in all you do"

http://devlicio.us/blogs/rob_reynolds
http://ferventcoder.com
http://twitter.com/ferventcoder