Skip to content

Commit

Permalink
Initialize MEF services before switching to the main thread
Browse files Browse the repository at this point in the history
  • Loading branch information
sharwell committed Jun 17, 2021
1 parent 6920e1e commit 7d1ea1b
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/VisualStudio/Core/Impl/CodeModel/CodeModelState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public CodeModelState(
Debug.Assert(languageServices != null);
Debug.Assert(workspace != null);

// ⚠ This code runs on the main thread. Language services accessed here should be preloaded in
// ProjectCodemodelFactory to avoid blocking MEF operations.
this.ThreadingContext = threadingContext;
this.ServiceProvider = serviceProvider;
this.CodeModelService = languageServices.GetService<ICodeModelService>();
Expand Down
28 changes: 26 additions & 2 deletions src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Threading;
using Roslyn.Utilities;
using Task = System.Threading.Tasks.Task;

Expand Down Expand Up @@ -76,6 +79,27 @@ private async Task ProcessNextDocumentBatchAsync(
const int MaxTimeSlice = 15;
var delayBetweenProcessing = TimeSpan.FromMilliseconds(50);

Debug.Assert(!_threadingContext.JoinableTaskContext.IsOnMainThread, "The following context switch is not expected to cause runtime overhead.");
await TaskScheduler.Default;

// Ensure MEF services used by the code model are initially obtained on a background thread.
// This code avoids allocations where possible.
// https://github.com/dotnet/roslyn/issues/54159
string? previousLanguage = null;
foreach (var (_, projectState) in _visualStudioWorkspace.CurrentSolution.State.ProjectStates)
{
if (projectState.Language == previousLanguage)
{
// Avoid duplicate calls if the language did not change
continue;
}

previousLanguage = projectState.Language;
projectState.LanguageServices.GetService<ICodeModelService>();
projectState.LanguageServices.GetService<ISyntaxFactsService>();
projectState.LanguageServices.GetService<ICodeGenerationService>();
}

await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);

var stopwatch = SharedStopwatch.StartNew();
Expand Down

0 comments on commit 7d1ea1b

Please sign in to comment.