Skip to content

Commit

Permalink
Fix deadlock when getting parameters in an event
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
SeeminglyScience committed Oct 20, 2018
1 parent 03185db commit 615c540
Showing 1 changed file with 23 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<string[]>("ProviderName");
}
set {
Expand Down Expand Up @@ -103,7 +103,7 @@ protected virtual IEnumerable<PackageProvider> 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();

Expand All @@ -116,7 +116,7 @@ protected virtual IEnumerable<PackageProvider> 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;
Expand Down Expand Up @@ -438,19 +438,30 @@ public override bool GenerateDynamicParameters() {
// any access to MyInvocation.MyCommand.*
// modifying parameter validation sets
//
Dictionary<string, ParameterMetadata> 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");
}
}
}
Expand All @@ -477,10 +488,10 @@ public override bool GenerateDynamicParameters() {
{
ProviderName = new[] { pName.ToString() };
}

// a user specifies -providerName
_isUserSpecifyOneProviderName = (ProviderName.Count() == 1);

}
}

Expand All @@ -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)
Expand Down Expand Up @@ -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)
{
Expand Down

0 comments on commit 615c540

Please sign in to comment.