-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Move to instantiating and setting up the language service on a bg thread #73351
Changes from 11 commits
3fd4789
b70f2a1
9bbab46
410b47f
987d373
92d23c2
2958760
be8f1be
bf83d82
1d84147
945de16
27c0bca
3e35578
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,8 @@ | |
using System; | ||
using System.Diagnostics; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Editor.Shared.Extensions; | ||
using Microsoft.CodeAnalysis.Editor.Shared.Utilities; | ||
|
@@ -19,13 +21,12 @@ | |
using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; | ||
using Microsoft.VisualStudio.Editor; | ||
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; | ||
using Microsoft.VisualStudio.LanguageServices.Implementation.TaskList; | ||
using Microsoft.VisualStudio.LanguageServices.Implementation.Venus; | ||
using Microsoft.VisualStudio.Shell; | ||
using Microsoft.VisualStudio.Shell.Interop; | ||
using Microsoft.VisualStudio.Text.Editor; | ||
using Microsoft.VisualStudio.Text.Outlining; | ||
using Microsoft.VisualStudio.TextManager.Interop; | ||
using Microsoft.VisualStudio.Threading; | ||
using Microsoft.VisualStudio.Utilities; | ||
using Roslyn.Utilities; | ||
|
||
|
@@ -36,7 +37,8 @@ internal abstract partial class AbstractLanguageService<TPackage, TLanguageServi | |
where TLanguageService : AbstractLanguageService<TPackage, TLanguageService> | ||
{ | ||
internal TPackage Package { get; } | ||
internal VsLanguageDebugInfo LanguageDebugInfo { get; private set; } | ||
|
||
private readonly VsLanguageDebugInfo _languageDebugInfo; | ||
|
||
// DevDiv 753309: | ||
// We've redefined some VS interfaces that had incorrect PIAs. When | ||
|
@@ -56,10 +58,10 @@ internal abstract partial class AbstractLanguageService<TPackage, TLanguageServi | |
// Note: The lifetime for state in this class is carefully managed. For every bit of state | ||
// we set up, there is a corresponding tear down phase which deconstructs the state in the | ||
// reverse order it was created in. | ||
internal EditorOptionsService EditorOptionsService { get; private set; } | ||
internal VisualStudioWorkspaceImpl Workspace { get; private set; } | ||
internal IVsEditorAdaptersFactoryService EditorAdaptersFactoryService { get; private set; } | ||
internal AnalyzerFileWatcherService AnalyzerFileWatcherService { get; private set; } | ||
internal readonly EditorOptionsService EditorOptionsService; | ||
internal readonly VisualStudioWorkspaceImpl Workspace; | ||
internal readonly IVsEditorAdaptersFactoryService EditorAdaptersFactoryService; | ||
internal readonly AnalyzerFileWatcherService AnalyzerFileWatcherService; | ||
|
||
/// <summary> | ||
/// Whether or not we have been set up. This is set once everything is wired up and cleared once tear down has begun. | ||
|
@@ -71,9 +73,22 @@ internal abstract partial class AbstractLanguageService<TPackage, TLanguageServi | |
/// </remarks> | ||
private bool _isSetUp; | ||
|
||
protected abstract string ContentTypeName { get; } | ||
protected abstract string LanguageName { get; } | ||
protected abstract string RoslynLanguageName { get; } | ||
protected abstract Guid DebuggerLanguageId { get; } | ||
|
||
protected AbstractLanguageService(TPackage package) | ||
{ | ||
Package = package; | ||
|
||
Debug.Assert(!this.Package.JoinableTaskFactory.Context.IsOnMainThread, "Language service should be instantiated on background thread"); | ||
|
||
this.EditorOptionsService = this.Package.ComponentModel.GetService<EditorOptionsService>(); | ||
this.Workspace = this.Package.ComponentModel.GetService<VisualStudioWorkspaceImpl>(); | ||
this.EditorAdaptersFactoryService = this.Package.ComponentModel.GetService<IVsEditorAdaptersFactoryService>(); | ||
this.AnalyzerFileWatcherService = this.Package.ComponentModel.GetService<AnalyzerFileWatcherService>(); | ||
this._languageDebugInfo = CreateLanguageDebugInfo(); | ||
sharwell marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
public override IServiceProvider SystemServiceProvider | ||
|
@@ -82,32 +97,28 @@ public override IServiceProvider SystemServiceProvider | |
/// <summary> | ||
/// Setup and TearDown go in reverse order. | ||
/// </summary> | ||
internal void Setup() | ||
public async Task SetupAsync(CancellationToken cancellationToken) | ||
{ | ||
this.ComAggregate = CreateComAggregate(); | ||
|
||
// First, acquire any services we need throughout our lifetime. | ||
this.GetServices(); | ||
// This method should only contain calls to acquire services off of the component model | ||
// or service providers. Anything else which is more complicated should go in Initialize | ||
// instead. | ||
|
||
// TODO: Is the below access to component model required or can be removed? | ||
_ = this.Package.ComponentModel; | ||
// Start off a background task to prime some components we'll need for editing. | ||
Task.Run(() => | ||
{ | ||
var formatter = this.Workspace.Services.GetLanguageServices(RoslynLanguageName).GetService<ISyntaxFormattingService>(); | ||
formatter?.GetDefaultFormattingRules(); | ||
}, cancellationToken).Forget(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. moved to normal Task.Run here. Could switch back to VsTaskLibraryHelper. Need to understand the value of that type. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fire and forget operations need to be tracked |
||
|
||
// Start off a background task to prime some components we'll need for editing | ||
VsTaskLibraryHelper.CreateAndStartTask(VsTaskLibraryHelper.ServiceInstance, VsTaskRunContext.BackgroundThread, | ||
() => PrimeLanguageServiceComponentsOnBackground()); | ||
await this.Package.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); | ||
|
||
// Finally, once our connections are established, set up any initial state that we need. | ||
// Note: we may be instantiated at any time (including when the IDE is already | ||
// debugging). We must not assume anything about our initial state and must instead | ||
// query for all the information we need at this point. | ||
this.Initialize(); | ||
// Creating the com aggregate has to happen on the UI thread. | ||
this.ComAggregate = Interop.ComAggregate.CreateAggregatedObject(this); | ||
|
||
_isSetUp = true; | ||
} | ||
|
||
private object CreateComAggregate() | ||
=> Interop.ComAggregate.CreateAggregatedObject(this); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. inlined this. |
||
|
||
internal void TearDown() | ||
{ | ||
if (!_isSetUp) | ||
|
@@ -117,9 +128,6 @@ internal void TearDown() | |
|
||
_isSetUp = false; | ||
GC.SuppressFinalize(this); | ||
|
||
this.Uninitialize(); | ||
this.RemoveServices(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need for these. all they did was null out managed objects. something unnecessary with managed types and disposal. |
||
} | ||
|
||
~AbstractLanguageService() | ||
|
@@ -130,50 +138,6 @@ internal void TearDown() | |
} | ||
} | ||
|
||
protected virtual void GetServices() | ||
{ | ||
// This method should only contain calls to acquire services off of the component model | ||
// or service providers. Anything else which is more complicated should go in Initialize | ||
// instead. | ||
this.EditorOptionsService = this.Package.ComponentModel.GetService<EditorOptionsService>(); | ||
this.Workspace = this.Package.ComponentModel.GetService<VisualStudioWorkspaceImpl>(); | ||
this.EditorAdaptersFactoryService = this.Package.ComponentModel.GetService<IVsEditorAdaptersFactoryService>(); | ||
this.AnalyzerFileWatcherService = this.Package.ComponentModel.GetService<AnalyzerFileWatcherService>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. inlined into constructor. |
||
} | ||
|
||
protected virtual void RemoveServices() | ||
{ | ||
this.EditorAdaptersFactoryService = null; | ||
this.Workspace = null; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this doesn't seem to serve any purpose. nulling out these simple managed types serves no purpose. |
||
|
||
/// <summary> | ||
/// Called right after we instantiate the language service. Used to set up any internal | ||
/// state we need. | ||
/// | ||
/// Try to keep this method fairly clean. Any complicated logic should go in methods called | ||
/// from this one. Initialize and Uninitialize go in reverse order | ||
/// </summary> | ||
protected virtual void Initialize() | ||
{ | ||
InitializeLanguageDebugInfo(); | ||
} | ||
|
||
protected virtual void Uninitialize() | ||
{ | ||
UninitializeLanguageDebugInfo(); | ||
} | ||
|
||
private void PrimeLanguageServiceComponentsOnBackground() | ||
{ | ||
var formatter = this.Workspace.Services.GetLanguageServices(RoslynLanguageName).GetService<ISyntaxFormattingService>(); | ||
formatter?.GetDefaultFormattingRules(); | ||
} | ||
|
||
protected abstract string ContentTypeName { get; } | ||
protected abstract string LanguageName { get; } | ||
protected abstract string RoslynLanguageName { get; } | ||
|
||
protected virtual void SetupNewTextView(IVsTextView textView) | ||
{ | ||
Contract.ThrowIfNull(textView); | ||
|
@@ -255,15 +219,9 @@ private void ConditionallyCollapseOutliningRegions(IVsTextView textView, IWpfTex | |
} | ||
} | ||
|
||
private void InitializeLanguageDebugInfo() | ||
=> this.LanguageDebugInfo = this.CreateLanguageDebugInfo(); | ||
|
||
protected abstract Guid DebuggerLanguageId { get; } | ||
|
||
private VsLanguageDebugInfo CreateLanguageDebugInfo() | ||
{ | ||
var workspace = this.Workspace; | ||
var languageServices = workspace.Services.GetLanguageServices(RoslynLanguageName); | ||
var languageServices = this.Workspace.Services.GetLanguageServices(RoslynLanguageName); | ||
|
||
return new VsLanguageDebugInfo( | ||
this.DebuggerLanguageId, | ||
|
@@ -273,9 +231,6 @@ private VsLanguageDebugInfo CreateLanguageDebugInfo() | |
this.Package.ComponentModel.GetService<IUIThreadOperationExecutor>()); | ||
} | ||
|
||
private void UninitializeLanguageDebugInfo() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this uninitialize stuff doesn't appear to serve any purpose. nulling out simple managed types doesnt' buy us anything. |
||
=> this.LanguageDebugInfo = null; | ||
|
||
protected virtual IVsContainedLanguage CreateContainedLanguage( | ||
IVsTextBufferCoordinator bufferCoordinator, ProjectSystemProject project, | ||
IVsHierarchy hierarchy, uint itemid) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
by init'ing these in the ctor and not nulling them out, could this file be made nullable without too much effort now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes. will do in followup.