Skip to content

Commit b2d444c

Browse files
authored
Merge pull request #329 from PowerShell/kapilmb/fix-select-pssa-rules
Prevent concurrent commands in analysis services
2 parents 4e155bd + ee819f6 commit b2d444c

File tree

1 file changed

+106
-75
lines changed

1 file changed

+106
-75
lines changed

src/PowerShellEditorServices/Analysis/AnalysisService.cs

+106-75
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ public class AnalysisService : IDisposable
2626

2727
private Runspace analysisRunspace;
2828
private PSModuleInfo scriptAnalyzerModuleInfo;
29+
private Object runspaceLock;
30+
private string[] activeRules;
31+
private string settingsPath;
2932

3033
/// <summary>
3134
/// Defines the list of Script Analyzer rules to include by default if
@@ -54,16 +57,39 @@ public class AnalysisService : IDisposable
5457
/// <summary>
5558
/// Set of PSScriptAnalyzer rules used for analysis
5659
/// </summary>
57-
public string[] ActiveRules { get; set; }
60+
public string[] ActiveRules
61+
{
62+
get
63+
{
64+
return activeRules;
65+
}
66+
67+
set
68+
{
69+
lock (runspaceLock)
70+
{
71+
activeRules = value;
72+
}
73+
}
74+
}
5875

5976
/// <summary>
6077
/// Gets or sets the path to a settings file (.psd1)
6178
/// containing PSScriptAnalyzer settings.
6279
/// </summary>
6380
public string SettingsPath
6481
{
65-
get;
66-
set;
82+
get
83+
{
84+
return settingsPath;
85+
}
86+
set
87+
{
88+
lock (runspaceLock)
89+
{
90+
settingsPath = value;
91+
}
92+
}
6793
}
6894

6995
#endregion
@@ -80,6 +106,7 @@ public AnalysisService(IConsoleHost consoleHost, string settingsPath = null)
80106
{
81107
try
82108
{
109+
this.runspaceLock = new Object();
83110
this.SettingsPath = settingsPath;
84111
this.analysisRunspace = RunspaceFactory.CreateRunspace(InitialSessionState.CreateDefault2());
85112
this.analysisRunspace.ThreadOptions = PSThreadOptions.ReuseThread;
@@ -117,10 +144,10 @@ public ScriptFileMarker[] GetSemanticMarkers(ScriptFile file)
117144
Task.Factory.StartNew<ScriptFileMarker[]>(
118145
() =>
119146
{
120-
return
121-
GetDiagnosticRecords(file)
122-
.Select(ScriptFileMarker.FromDiagnosticRecord)
123-
.ToArray();
147+
return
148+
GetDiagnosticRecords(file)
149+
.Select(ScriptFileMarker.FromDiagnosticRecord)
150+
.ToArray();
124151
},
125152
CancellationToken.None,
126153
TaskCreationOptions.None,
@@ -143,13 +170,16 @@ public IEnumerable<string> GetPSScriptAnalyzerRules()
143170
List<string> ruleNames = new List<string>();
144171
if (scriptAnalyzerModuleInfo != null)
145172
{
146-
using (var ps = System.Management.Automation.PowerShell.Create())
173+
lock (runspaceLock)
147174
{
148-
ps.Runspace = this.analysisRunspace;
149-
var ruleObjects = ps.AddCommand("Get-ScriptAnalyzerRule").Invoke();
150-
foreach (var rule in ruleObjects)
175+
using (var ps = System.Management.Automation.PowerShell.Create())
151176
{
152-
ruleNames.Add((string)rule.Members["RuleName"].Value);
177+
ps.Runspace = this.analysisRunspace;
178+
var ruleObjects = ps.AddCommand("Get-ScriptAnalyzerRule").Invoke();
179+
foreach (var rule in ruleObjects)
180+
{
181+
ruleNames.Add((string)rule.Members["RuleName"].Value);
182+
}
153183
}
154184
}
155185
}
@@ -175,38 +205,33 @@ public void Dispose()
175205
#region Private Methods
176206
private void FindPSScriptAnalyzer()
177207
{
178-
using (var ps = System.Management.Automation.PowerShell.Create())
208+
lock (runspaceLock)
179209
{
180-
ps.Runspace = this.analysisRunspace;
181-
182-
ps.AddCommand("Get-Module")
183-
.AddParameter("ListAvailable")
184-
.AddParameter("Name", "PSScriptAnalyzer");
185-
186-
ps.AddCommand("Sort-Object")
187-
.AddParameter("Descending")
188-
.AddParameter("Property", "Version");
189-
190-
ps.AddCommand("Select-Object")
191-
.AddParameter("First", 1);
210+
using (var ps = System.Management.Automation.PowerShell.Create())
211+
{
212+
ps.Runspace = this.analysisRunspace;
192213

193-
var modules = ps.Invoke();
214+
var modules = ps.AddCommand("Get-Module")
215+
.AddParameter("List")
216+
.AddParameter("Name", "PSScriptAnalyzer")
217+
.Invoke();
194218

195-
var psModule = modules == null ? null : modules.FirstOrDefault();
196-
if (psModule != null)
197-
{
198-
scriptAnalyzerModuleInfo = psModule.ImmediateBaseObject as PSModuleInfo;
199-
Logger.Write(
200-
LogLevel.Normal,
201-
string.Format(
202-
"PSScriptAnalyzer found at {0}",
203-
scriptAnalyzerModuleInfo.Path));
204-
}
205-
else
206-
{
207-
Logger.Write(
208-
LogLevel.Normal,
209-
"PSScriptAnalyzer module was not found.");
219+
var psModule = modules == null ? null : modules.FirstOrDefault();
220+
if (psModule != null)
221+
{
222+
scriptAnalyzerModuleInfo = psModule.ImmediateBaseObject as PSModuleInfo;
223+
Logger.Write(
224+
LogLevel.Normal,
225+
string.Format(
226+
"PSScriptAnalyzer found at {0}",
227+
scriptAnalyzerModuleInfo.Path));
228+
}
229+
else
230+
{
231+
Logger.Write(
232+
LogLevel.Normal,
233+
"PSScriptAnalyzer module was not found.");
234+
}
210235
}
211236
}
212237
}
@@ -215,25 +240,28 @@ private void ImportPSScriptAnalyzer()
215240
{
216241
if (scriptAnalyzerModuleInfo != null)
217242
{
218-
using (var ps = System.Management.Automation.PowerShell.Create())
243+
lock (runspaceLock)
219244
{
220-
ps.Runspace = this.analysisRunspace;
245+
using (var ps = System.Management.Automation.PowerShell.Create())
246+
{
247+
ps.Runspace = this.analysisRunspace;
221248

222-
var module = ps.AddCommand("Import-Module")
223-
.AddParameter("ModuleInfo", scriptAnalyzerModuleInfo)
224-
.AddParameter("PassThru")
225-
.Invoke();
249+
var module = ps.AddCommand("Import-Module")
250+
.AddParameter("ModuleInfo", scriptAnalyzerModuleInfo)
251+
.AddParameter("PassThru")
252+
.Invoke();
226253

227-
if (module == null)
228-
{
229-
this.scriptAnalyzerModuleInfo = null;
230-
Logger.Write(LogLevel.Warning,
231-
String.Format("Cannot Import PSScriptAnalyzer: {0}"));
232-
}
233-
else
234-
{
235-
Logger.Write(LogLevel.Normal,
236-
String.Format("Successfully imported PSScriptAnalyzer"));
254+
if (module == null)
255+
{
256+
this.scriptAnalyzerModuleInfo = null;
257+
Logger.Write(LogLevel.Warning,
258+
String.Format("Cannot Import PSScriptAnalyzer: {0}"));
259+
}
260+
else
261+
{
262+
Logger.Write(LogLevel.Normal,
263+
String.Format("Successfully imported PSScriptAnalyzer"));
264+
}
237265
}
238266
}
239267
}
@@ -268,28 +296,31 @@ private IEnumerable<PSObject> GetDiagnosticRecords(ScriptFile file)
268296

269297
if (this.scriptAnalyzerModuleInfo != null)
270298
{
271-
using (var powerShell = System.Management.Automation.PowerShell.Create())
299+
lock (runspaceLock)
272300
{
273-
powerShell.Runspace = this.analysisRunspace;
274-
Logger.Write(
275-
LogLevel.Verbose,
276-
String.Format("Running PSScriptAnalyzer against {0}", file.FilePath));
301+
using (var powerShell = System.Management.Automation.PowerShell.Create())
302+
{
303+
powerShell.Runspace = this.analysisRunspace;
304+
Logger.Write(
305+
LogLevel.Verbose,
306+
String.Format("Running PSScriptAnalyzer against {0}", file.FilePath));
277307

278-
powerShell
279-
.AddCommand("Invoke-ScriptAnalyzer")
280-
.AddParameter("ScriptDefinition", file.Contents);
308+
powerShell
309+
.AddCommand("Invoke-ScriptAnalyzer")
310+
.AddParameter("ScriptDefinition", file.Contents);
281311

282-
// Use a settings file if one is provided, otherwise use the default rule list.
283-
if (!string.IsNullOrWhiteSpace(this.SettingsPath))
284-
{
285-
powerShell.AddParameter("Settings", this.SettingsPath);
286-
}
287-
else
288-
{
289-
powerShell.AddParameter("IncludeRule", ActiveRules);
290-
}
312+
// Use a settings file if one is provided, otherwise use the default rule list.
313+
if (!string.IsNullOrWhiteSpace(this.SettingsPath))
314+
{
315+
powerShell.AddParameter("Settings", this.SettingsPath);
316+
}
317+
else
318+
{
319+
powerShell.AddParameter("IncludeRule", activeRules);
320+
}
291321

292-
diagnosticRecords = powerShell.Invoke();
322+
diagnosticRecords = powerShell.Invoke();
323+
}
293324
}
294325
}
295326

0 commit comments

Comments
 (0)