Skip to content

Prevent concurrent commands in analysis services #329

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 20, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 106 additions & 75 deletions src/PowerShellEditorServices/Analysis/AnalysisService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ public class AnalysisService : IDisposable

private Runspace analysisRunspace;
private PSModuleInfo scriptAnalyzerModuleInfo;
private Object runspaceLock;
private string[] activeRules;
private string settingsPath;

/// <summary>
/// Defines the list of Script Analyzer rules to include by default if
Expand Down Expand Up @@ -54,16 +57,39 @@ public class AnalysisService : IDisposable
/// <summary>
/// Set of PSScriptAnalyzer rules used for analysis
/// </summary>
public string[] ActiveRules { get; set; }
public string[] ActiveRules
{
get
{
return activeRules;
}

set
{
lock (runspaceLock)
{
activeRules = value;
}
}
}

/// <summary>
/// Gets or sets the path to a settings file (.psd1)
/// containing PSScriptAnalyzer settings.
/// </summary>
public string SettingsPath
{
get;
set;
get
{
return settingsPath;
}
set
{
lock (runspaceLock)
{
settingsPath = value;
}
}
}

#endregion
Expand All @@ -80,6 +106,7 @@ public AnalysisService(IConsoleHost consoleHost, string settingsPath = null)
{
try
{
this.runspaceLock = new Object();
this.SettingsPath = settingsPath;
this.analysisRunspace = RunspaceFactory.CreateRunspace(InitialSessionState.CreateDefault2());
this.analysisRunspace.ThreadOptions = PSThreadOptions.ReuseThread;
Expand Down Expand Up @@ -117,10 +144,10 @@ public ScriptFileMarker[] GetSemanticMarkers(ScriptFile file)
Task.Factory.StartNew<ScriptFileMarker[]>(
() =>
{
return
GetDiagnosticRecords(file)
.Select(ScriptFileMarker.FromDiagnosticRecord)
.ToArray();
return
GetDiagnosticRecords(file)
.Select(ScriptFileMarker.FromDiagnosticRecord)
.ToArray();
},
CancellationToken.None,
TaskCreationOptions.None,
Expand All @@ -143,13 +170,16 @@ public IEnumerable<string> GetPSScriptAnalyzerRules()
List<string> ruleNames = new List<string>();
if (scriptAnalyzerModuleInfo != null)
{
using (var ps = System.Management.Automation.PowerShell.Create())
lock (runspaceLock)
{
ps.Runspace = this.analysisRunspace;
var ruleObjects = ps.AddCommand("Get-ScriptAnalyzerRule").Invoke();
foreach (var rule in ruleObjects)
using (var ps = System.Management.Automation.PowerShell.Create())
{
ruleNames.Add((string)rule.Members["RuleName"].Value);
ps.Runspace = this.analysisRunspace;
var ruleObjects = ps.AddCommand("Get-ScriptAnalyzerRule").Invoke();
foreach (var rule in ruleObjects)
{
ruleNames.Add((string)rule.Members["RuleName"].Value);
}
}
}
}
Expand All @@ -175,38 +205,33 @@ public void Dispose()
#region Private Methods
private void FindPSScriptAnalyzer()
{
using (var ps = System.Management.Automation.PowerShell.Create())
lock (runspaceLock)
{
ps.Runspace = this.analysisRunspace;

ps.AddCommand("Get-Module")
.AddParameter("ListAvailable")
.AddParameter("Name", "PSScriptAnalyzer");

ps.AddCommand("Sort-Object")
.AddParameter("Descending")
.AddParameter("Property", "Version");

ps.AddCommand("Select-Object")
.AddParameter("First", 1);
using (var ps = System.Management.Automation.PowerShell.Create())
{
ps.Runspace = this.analysisRunspace;

var modules = ps.Invoke();
var modules = ps.AddCommand("Get-Module")
.AddParameter("List")
.AddParameter("Name", "PSScriptAnalyzer")
.Invoke();

var psModule = modules == null ? null : modules.FirstOrDefault();
if (psModule != null)
{
scriptAnalyzerModuleInfo = psModule.ImmediateBaseObject as PSModuleInfo;
Logger.Write(
LogLevel.Normal,
string.Format(
"PSScriptAnalyzer found at {0}",
scriptAnalyzerModuleInfo.Path));
}
else
{
Logger.Write(
LogLevel.Normal,
"PSScriptAnalyzer module was not found.");
var psModule = modules == null ? null : modules.FirstOrDefault();
if (psModule != null)
{
scriptAnalyzerModuleInfo = psModule.ImmediateBaseObject as PSModuleInfo;
Logger.Write(
LogLevel.Normal,
string.Format(
"PSScriptAnalyzer found at {0}",
scriptAnalyzerModuleInfo.Path));
}
else
{
Logger.Write(
LogLevel.Normal,
"PSScriptAnalyzer module was not found.");
}
}
}
}
Expand All @@ -215,25 +240,28 @@ private void ImportPSScriptAnalyzer()
{
if (scriptAnalyzerModuleInfo != null)
{
using (var ps = System.Management.Automation.PowerShell.Create())
lock (runspaceLock)
{
ps.Runspace = this.analysisRunspace;
using (var ps = System.Management.Automation.PowerShell.Create())
{
ps.Runspace = this.analysisRunspace;

var module = ps.AddCommand("Import-Module")
.AddParameter("ModuleInfo", scriptAnalyzerModuleInfo)
.AddParameter("PassThru")
.Invoke();
var module = ps.AddCommand("Import-Module")
.AddParameter("ModuleInfo", scriptAnalyzerModuleInfo)
.AddParameter("PassThru")
.Invoke();

if (module == null)
{
this.scriptAnalyzerModuleInfo = null;
Logger.Write(LogLevel.Warning,
String.Format("Cannot Import PSScriptAnalyzer: {0}"));
}
else
{
Logger.Write(LogLevel.Normal,
String.Format("Successfully imported PSScriptAnalyzer"));
if (module == null)
{
this.scriptAnalyzerModuleInfo = null;
Logger.Write(LogLevel.Warning,
String.Format("Cannot Import PSScriptAnalyzer: {0}"));
}
else
{
Logger.Write(LogLevel.Normal,
String.Format("Successfully imported PSScriptAnalyzer"));
}
}
}
}
Expand Down Expand Up @@ -268,28 +296,31 @@ private IEnumerable<PSObject> GetDiagnosticRecords(ScriptFile file)

if (this.scriptAnalyzerModuleInfo != null)
{
using (var powerShell = System.Management.Automation.PowerShell.Create())
lock (runspaceLock)
{
powerShell.Runspace = this.analysisRunspace;
Logger.Write(
LogLevel.Verbose,
String.Format("Running PSScriptAnalyzer against {0}", file.FilePath));
using (var powerShell = System.Management.Automation.PowerShell.Create())
{
powerShell.Runspace = this.analysisRunspace;
Logger.Write(
LogLevel.Verbose,
String.Format("Running PSScriptAnalyzer against {0}", file.FilePath));

powerShell
.AddCommand("Invoke-ScriptAnalyzer")
.AddParameter("ScriptDefinition", file.Contents);
powerShell
.AddCommand("Invoke-ScriptAnalyzer")
.AddParameter("ScriptDefinition", file.Contents);

// Use a settings file if one is provided, otherwise use the default rule list.
if (!string.IsNullOrWhiteSpace(this.SettingsPath))
{
powerShell.AddParameter("Settings", this.SettingsPath);
}
else
{
powerShell.AddParameter("IncludeRule", ActiveRules);
}
// Use a settings file if one is provided, otherwise use the default rule list.
if (!string.IsNullOrWhiteSpace(this.SettingsPath))
{
powerShell.AddParameter("Settings", this.SettingsPath);
}
else
{
powerShell.AddParameter("IncludeRule", activeRules);
}

diagnosticRecords = powerShell.Invoke();
diagnosticRecords = powerShell.Invoke();
}
}
}

Expand Down