From 615c540dbdeda9b001c51e6f7d1ea55cbbc9ebc2 Mon Sep 17 00:00:00 2001 From: Patrick Meinecke Date: Sat, 20 Oct 2018 07:50:02 -0400 Subject: [PATCH] Fix deadlock when getting parameters in an event When getting dynamic parameters from a thread other than the pipeline thread, the call to CommandInfo.Parameters internally tries to get back to it using the PowerShell EventManager. If dynamic parameters are being obtained from an event that is being processed by the EventManager then a deadlock will occur. This change routes the call to CommandInfo.Parameters to the main thread using AsyncCmdlet.ExecuteOnMainThread, bypassing the EventManager. --- .../Cmdlets/CmdletWithProvider.cs | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/Microsoft.PowerShell.PackageManagement/Cmdlets/CmdletWithProvider.cs b/src/Microsoft.PowerShell.PackageManagement/Cmdlets/CmdletWithProvider.cs index d627ac2a5..6a80b07d6 100644 --- a/src/Microsoft.PowerShell.PackageManagement/Cmdlets/CmdletWithProvider.cs +++ b/src/Microsoft.PowerShell.PackageManagement/Cmdlets/CmdletWithProvider.cs @@ -51,7 +51,7 @@ public string[] ProviderName { //meaning a user specifies -ProviderName, we will use it return _providerName; } - //need to call it so that cases like get-packagesource | find-package -name Jquery will work + //need to call it so that cases like get-packagesource | find-package -name Jquery will work return GetDynamicParameterValue("ProviderName"); } set { @@ -103,7 +103,7 @@ protected virtual IEnumerable SelectedProviders { // save the resolved package source name and location potentialSources.SerialForEach(src => UserSpecifiedSourcesList.AddOrSet(src.Name, src.Location)); - + // prefer registered sources registeredSources = potentialSources.Where(source => source.IsRegistered).ReEnumerable(); @@ -116,7 +116,7 @@ protected virtual IEnumerable SelectedProviders { if (!didUserSpecifyProviders) { // if cmdlet is generating parameter, we have to log error that source is wrong if (IsInvocation && CmdletState >= AsyncCmdletState.GenerateParameters ) { - + // user didn't actually specify provider(s), the sources can't be tied to any particular provider QueueHeldMessage(() => Error(Constants.Errors.SourceNotFound, userSpecifiedSources.JoinWithComma())); IsFailingEarly = true; @@ -438,19 +438,30 @@ public override bool GenerateDynamicParameters() { // any access to MyInvocation.MyCommand.* // modifying parameter validation sets // + Dictionary parameters = null; + + // If MyInvocation.MyCommand.Parameters is not called from the pipeline thread + // then it is rerouted back to the pipeline thread via the EventManager. This + // can cause a deadlock in the rare case that the EventManager is already busy. + ExecuteOnMainThread( + () => + { + parameters = MyInvocation?.MyCommand?.Parameters; + return true; + }).GetAwaiter().GetResult(); - if (MyInvocation != null && MyInvocation.MyCommand != null && MyInvocation.MyCommand.Parameters != null) { - GetType().AddOrSet(MyInvocation.MyCommand.Parameters, "MyInvocation.MyCommand.Parameters"); + if (parameters != null) { + GetType().AddOrSet(parameters, "MyInvocation.MyCommand.Parameters"); } #if DEEP_DEBUG else { if (MyInvocation == null) { - Console.WriteLine("��� Attempt to get parameters MyInvocation == NULL"); + Console.WriteLine("��� Attempt to get parameters MyInvocation == NULL"); } else { if (MyInvocation.MyCommand == null) { - Console.WriteLine("��� Attempt to get parameters MyCommand == NULL"); + Console.WriteLine("��� Attempt to get parameters MyCommand == NULL"); } else { - Console.WriteLine("��� Attempt to get parameters Parameters == NULL"); + Console.WriteLine("��� Attempt to get parameters Parameters == NULL"); } } } @@ -477,10 +488,10 @@ public override bool GenerateDynamicParameters() { { ProviderName = new[] { pName.ToString() }; } - + // a user specifies -providerName _isUserSpecifyOneProviderName = (ProviderName.Count() == 1); - + } } @@ -490,7 +501,7 @@ public override bool GenerateDynamicParameters() { if (null== CachedSelectedProviders || IsFailingEarly || IsCanceled ) { #if DEEP_DEBUG - Console.WriteLine("��� Cancelled before we got finished doing dynamic parameters"); + Console.WriteLine("��� Cancelled before we got finished doing dynamic parameters"); #endif // this happens if there is a serious failure early in the cmdlet // i.e. - if the SelectedProviders comes back empty (due to aggressive filtering) @@ -574,7 +585,7 @@ private string GetSoftwareIdentityTypeName(SoftwareIdentity package) return _softwareIdentityTypeName; } - //check if a user specifies -source with a long source name such as http://www.powershellgallery.com/api/v2, + //check if a user specifies -source with a long source name such as http://www.powershellgallery.com/api/v2, // if so we will choose the longsource column format. if (!UseDefaultSourceFormat) {