-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Further clean up Roslyn's package load #80598
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
Changes from all commits
0b44805
ee06fd9
a0353c9
1b2c755
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,7 @@ | |
| using System.Threading.Tasks; | ||
| using Microsoft.VisualStudio.ComponentModelHost; | ||
| using Microsoft.VisualStudio.Shell; | ||
| using Microsoft.VisualStudio.Shell.Interop; | ||
|
|
||
| namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService; | ||
|
|
||
|
|
@@ -51,26 +52,34 @@ private Task RegisterAndProcessTasksAsync(Action<PackageLoadTasks> registerTasks | |
|
|
||
| protected virtual void RegisterInitializeAsyncWork(PackageLoadTasks packageInitializationTasks) | ||
| { | ||
| // This treatment of registering work on the bg/main threads is a bit unique as we want the component model initialized at the beginning | ||
| // of whichever context is invoked first. The current architecture doesn't execute any of the registered tasks concurrently, | ||
| // so that isn't a concern for calculating or setting _componentModel_doNotAccessDirectly multiple times. | ||
| // We register this task so our ComponentModel property is available during other parts of package initialization and OnAfterPackageLoaded work. The | ||
| // expectation at this point is no work scheduled to the UI thread needs the ComponentModel, so we only schedule it for the background thread. | ||
| packageInitializationTasks.AddTask(isMainThreadTask: false, task: EnsureComponentModelAsync); | ||
| packageInitializationTasks.AddTask(isMainThreadTask: true, task: EnsureComponentModelAsync); | ||
jasonmalinowski marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| async Task EnsureComponentModelAsync(PackageLoadTasks packageInitializationTasks, CancellationToken token) | ||
| { | ||
| if (_componentModel_doNotAccessDirectly == null) | ||
| { | ||
| _componentModel_doNotAccessDirectly = (IComponentModel?)await GetServiceAsync(typeof(SComponentModel)).ConfigureAwait(false); | ||
| Assumes.Present(_componentModel_doNotAccessDirectly); | ||
| } | ||
| _componentModel_doNotAccessDirectly = (IComponentModel?)await GetServiceAsync(typeof(SComponentModel)).ConfigureAwait(false); | ||
| Assumes.Present(_componentModel_doNotAccessDirectly); | ||
| } | ||
| } | ||
|
|
||
| protected virtual void RegisterOnAfterPackageLoadedAsyncWork(PackageLoadTasks afterPackageLoadedTasks) | ||
| { | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Registers an editor factory. This is the same as <see cref="Microsoft.VisualStudio.Shell.Package.RegisterEditorFactory(IVsEditorFactory)"/> except it fetches the service async. | ||
| /// </summary> | ||
| protected async Task RegisterEditorFactoryAsync(IVsEditorFactory editorFactory, CancellationToken cancellationToken) | ||
| { | ||
| // Call with ConfigureAwait(true): if we're off the UI thread we will stay that way, but a synchronous load of our package should continue to use the UI thread | ||
| // since the UI thread is otherwise blocked waiting for us. This method is called under JTF rules so that's fine. | ||
| var registerEditors = await GetServiceAsync<SVsRegisterEditors, IVsRegisterEditors>(throwOnFailure: true, cancellationToken).ConfigureAwait(true); | ||
jasonmalinowski marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Assumes.Present(registerEditors); | ||
|
|
||
| ErrorHandler.ThrowOnFailure(registerEditors.RegisterEditor(editorFactory.GetType().GUID, editorFactory, out _)); | ||
|
Contributor
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. Looks like this was made threadsafe a couple months ago, so this is nice! Not related to this PR, but when looking this up, the first usage of RegisterEditor I found was still doing a main thread switch before calling RegisterEditor in VSCorePackage.InitializeAsync, which is a core package load sequence so I would have thought DavKean would have ensured it was changed already. Which makes me question whether I understand whether this is thread safe. I guess I'm also a little confused why this RegisterEditorFactoryAsync isn't defined in the Shell's Package class. Maybe the additional public surface area wasn't worth it for getting the IVsRegisterEditors via GetServiceAsync?
Member
Author
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. Yes, there really should be a method we can call here. FYI to @davkean for the other questions. |
||
| } | ||
|
|
||
| protected async Task LoadComponentsInUIContextOnceSolutionFullyLoadedAsync(CancellationToken cancellationToken) | ||
| { | ||
| // UIContexts can be "zombied" if UIContexts aren't supported because we're in a command line build or in other scenarios. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,7 +12,6 @@ | |
| using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion; | ||
| using Microsoft.CodeAnalysis.Editor.Shared.Utilities; | ||
| using Microsoft.CodeAnalysis.ErrorReporting; | ||
| using Microsoft.CodeAnalysis.Options; | ||
| using Microsoft.CodeAnalysis.Remote.ProjectSystem; | ||
| using Microsoft.VisualStudio.LanguageServices.EditorConfigSettings; | ||
| using Microsoft.VisualStudio.LanguageServices.ExternalAccess.UnitTesting; | ||
|
|
@@ -69,41 +68,24 @@ protected override void RegisterInitializeAsyncWork(PackageLoadTasks packageInit | |
| base.RegisterInitializeAsyncWork(packageInitializationTasks); | ||
|
|
||
| packageInitializationTasks.AddTask(isMainThreadTask: false, task: PackageInitializationBackgroundThreadAsync); | ||
| packageInitializationTasks.AddTask(isMainThreadTask: true, task: PackageInitializationMainThreadAsync); | ||
|
Member
Author
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. Woohoo! Nothing needed here anymore. |
||
|
|
||
| return; | ||
|
|
||
| Task PackageInitializationBackgroundThreadAsync(PackageLoadTasks packageInitializationTasks, CancellationToken cancellationToken) | ||
| async Task PackageInitializationBackgroundThreadAsync(PackageLoadTasks packageInitializationTasks, CancellationToken cancellationToken) | ||
| { | ||
| return ProfferServiceBrokerServicesAsync(cancellationToken); | ||
| } | ||
|
|
||
| Task PackageInitializationMainThreadAsync(PackageLoadTasks packageInitializationTasks, CancellationToken cancellationToken) | ||
| { | ||
| var settingsEditorFactory = SettingsEditorFactory.GetInstance(); | ||
| RegisterEditorFactory(settingsEditorFactory); | ||
|
|
||
| return Task.CompletedTask; | ||
| await RegisterEditorFactoryAsync(new SettingsEditorFactory(), cancellationToken).ConfigureAwait(true); | ||
| await ProfferServiceBrokerServicesAsync(cancellationToken).ConfigureAwait(true); | ||
| } | ||
| } | ||
|
|
||
| protected override void RegisterOnAfterPackageLoadedAsyncWork(PackageLoadTasks afterPackageLoadedTasks) | ||
| { | ||
| base.RegisterOnAfterPackageLoadedAsyncWork(afterPackageLoadedTasks); | ||
|
|
||
| afterPackageLoadedTasks.AddTask(isMainThreadTask: false, task: OnAfterPackageLoadedBackgroundThreadAsync); | ||
| afterPackageLoadedTasks.AddTask(isMainThreadTask: true, task: OnAfterPackageLoadedMainThreadAsync); | ||
|
Member
Author
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 will be a follow-up to switch this to a background thread, since LoadComponentsInUIContextOnceSolutionFullyLoadedAsync is just waiting for a UIContext to be active before doing more background loading. |
||
|
|
||
| return; | ||
|
|
||
| Task OnAfterPackageLoadedBackgroundThreadAsync(PackageLoadTasks afterPackageLoadedTasks, CancellationToken cancellationToken) | ||
| { | ||
| // Ensure the options persisters are loaded since we have to fetch options from the shell | ||
| _ = ComponentModel.GetService<IGlobalOptionService>(); | ||
jasonmalinowski marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| return Task.CompletedTask; | ||
| } | ||
|
|
||
| Task OnAfterPackageLoadedMainThreadAsync(PackageLoadTasks afterPackageLoadedTasks, CancellationToken cancellationToken) | ||
| { | ||
| // load some services that have to be loaded in UI thread | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
| // See the LICENSE file in the project root for more information. | ||
|
|
||
| using System; | ||
| using System.Runtime.InteropServices; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using Microsoft.VisualStudio.Shell; | ||
|
|
||
| namespace Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; | ||
|
|
||
| internal static class CommandLineMode | ||
| { | ||
| // tri-state: uninitialized (0), devenv is in command line mode (1), devenv is not in command line mode (-1) | ||
| private static volatile int s_isInCommandLineMode; | ||
jasonmalinowski marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /// <summary> | ||
| /// Returns true if devenv is invoked in command line mode for build, e.g. devenv /rebuild MySolution.sln | ||
| /// </summary> | ||
| public static async Task<bool> IsInCommandLineModeAsync(IAsyncServiceProvider serviceProvider, CancellationToken cancellationToken) | ||
| { | ||
| if (s_isInCommandLineMode == 0) | ||
| { | ||
| var appId = await serviceProvider.GetServiceAsync<IVsAppId, IVsAppId>(cancellationToken).ConfigureAwait(true); | ||
|
|
||
| s_isInCommandLineMode = | ||
| ErrorHandler.Succeeded(appId.GetProperty(VSAPROPID_IsInCommandLineMode, out var result)) && | ||
| (bool)result ? 1 : -1; | ||
| } | ||
|
|
||
| return s_isInCommandLineMode == 1; | ||
| } | ||
|
|
||
| // Copied from https://github.com/dotnet/project-system/blob/698c90fc016a24fd5b0b2b73df2c68299e04bd66/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Interop/IVsAppId.cs | ||
| [Guid("1EAA526A-0898-11d3-B868-00C04F79F802"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] | ||
| internal interface IVsAppId | ||
| { | ||
| [PreserveSig] | ||
| int SetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider pSP); | ||
|
|
||
| [PreserveSig] | ||
| int GetProperty(int propid, // VSAPROPID | ||
| [MarshalAs(UnmanagedType.Struct)] out object pvar); | ||
|
|
||
| [PreserveSig] | ||
| int SetProperty(int propid, //[in] VSAPROPID | ||
| [MarshalAs(UnmanagedType.Struct)] object var); | ||
|
|
||
| [PreserveSig] | ||
| int GetGuidProperty(int propid, // VSAPROPID | ||
| out Guid guid); | ||
|
|
||
| [PreserveSig] | ||
| int SetGuidProperty(int propid, // [in] VSAPROPID | ||
| ref Guid rguid); | ||
|
|
||
| [PreserveSig] | ||
| int Initialize(); // called after main initialization and before command executing and entering main loop | ||
| } | ||
|
|
||
| private const int VSAPROPID_IsInCommandLineMode = -8660; | ||
| } | ||
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.