Skip to content

Commit 99b5746

Browse files
authored
Merge pull request #4 from immense/fix/workspace-pipeline-thread
fix: run WorkspaceService PSCommands on the pipeline thread
2 parents c088b3a + 0133b0d commit 99b5746

2 files changed

Lines changed: 26 additions & 6 deletions

File tree

‎src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ public PsesInternalHost(
226226

227227
public bool IsRunning => _isRunningLatch.IsSignaled;
228228

229+
internal bool IsPipelineThread => Environment.CurrentManagedThreadId == _pipelineThread.ManagedThreadId;
230+
229231
public Task Shutdown => _stopped.Task;
230232

231233
IRunspaceInfo IRunspaceContext.CurrentRunspace => CurrentRunspace;

‎src/PowerShellEditorServices/Services/Workspace/WorkspaceService.cs‎

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ public IEnumerable<string> EnumeratePSFiles(
447447
.AddParameter("EQ")
448448
.AddParameter("Value", false);
449449

450-
IReadOnlyList<PSObject> results = psesInternalHost.InvokePSCommand<PSObject>(psCommand, null, CancellationToken.None);
450+
IReadOnlyList<PSObject> results = ExecutePSCommandOnPipelineThread<PSObject>(psCommand, null, CancellationToken.None);
451451
foreach (string path in results.Select(ConvertWorkspaceItemPath).Where(path => !string.IsNullOrEmpty(path)))
452452
{
453453
yield return path;
@@ -510,7 +510,7 @@ internal string ReadFileContents(DocumentUri uri)
510510
string psPath = GetPowerShellPath(uri);
511511
try
512512
{
513-
IReadOnlyList<string> result = psesInternalHost.InvokePSCommand<string>(
513+
IReadOnlyList<string> result = ExecutePSCommandOnPipelineThread<string>(
514514
new PSCommand()
515515
.AddCommand(@"Microsoft.PowerShell.Management\Get-Content")
516516
.AddParameter("LiteralPath", psPath)
@@ -521,14 +521,32 @@ internal string ReadFileContents(DocumentUri uri)
521521
return string.Join(Environment.NewLine, result);
522522
}
523523
catch (ActionPreferenceStopException ex)
524-
when (ex.ErrorRecord.CategoryInfo.Category == ErrorCategory.ObjectNotFound
525-
&& ex.ErrorRecord.TargetObject is string[] missingFiles
526-
&& missingFiles.Length == 1)
524+
when (ex.ErrorRecord.CategoryInfo.Category == ErrorCategory.ObjectNotFound)
527525
{
528-
throw new FileNotFoundException(ex.ErrorRecord.ToString(), missingFiles[0], ex.ErrorRecord.Exception);
526+
// The FileSystem provider reports missing paths as string[]; other providers use a plain string.
527+
string missingFile = ex.ErrorRecord.TargetObject switch
528+
{
529+
string[] { Length: 1 } missingFiles => missingFiles[0],
530+
string missingPath => missingPath,
531+
_ => psPath,
532+
};
533+
534+
throw new FileNotFoundException(ex.ErrorRecord.ToString(), missingFile, ex.ErrorRecord.Exception);
529535
}
530536
}
531537

538+
// InvokePSCommand is only safe on the pipeline thread; queue onto it unless already there.
539+
private IReadOnlyList<TResult> ExecutePSCommandOnPipelineThread<TResult>(
540+
PSCommand psCommand,
541+
PowerShellExecutionOptions executionOptions,
542+
CancellationToken cancellationToken)
543+
{
544+
return psesInternalHost.IsPipelineThread
545+
? psesInternalHost.InvokePSCommand<TResult>(psCommand, executionOptions, cancellationToken)
546+
: psesInternalHost.ExecutePSCommandAsync<TResult>(psCommand, cancellationToken, executionOptions)
547+
.GetAwaiter().GetResult();
548+
}
549+
532550
// Return only file-backed workspace roots as filesystem paths.
533551
// Example:
534552
// file:///repo -> /repo

0 commit comments

Comments
 (0)