Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 40 additions & 25 deletions src/VisualStudio/Core/Def/LanguageService/AbstractPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,36 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Threading;

namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService;

internal abstract class AbstractPackage : AsyncPackage
{
private IComponentModel? _componentModel_doNotAccessDirectly;

internal IComponentModel ComponentModel
{
get
{
Assumes.Present(_componentModel_doNotAccessDirectly);
return _componentModel_doNotAccessDirectly;
if (field is null)
{
// We should have been initialized asynchronously, but somebody is asking earlier than we expected, so fetch it synchronously.
var componentModel = (IComponentModel?)GetService(typeof(SComponentModel));
Assumes.Present(componentModel);
field = componentModel;
return field;
}

return field;
}
Comment on lines 20 to +32
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this just be:

get => field ??= (IComponentModel?)GetService(typeof(SComponentModel)) ?? throw etc;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we weren't using Assumes.Present, yes....hmmm....

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's why I have the throw :-)


set
{
Assumes.Present(value);
field = value;
}
}

Expand Down Expand Up @@ -52,24 +66,33 @@ 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 have a legacy property to access the ComponentModel that is used across various parts of our load; we'll fetch this on the
// background thread as our first step so any later already has it available. If it were to be accessed by a UI-thread scheduled piece
// of work first, it'll still fetch it on demand.
packageInitializationTasks.AddTask(isMainThreadTask: false, task: EnsureComponentModelAsync);
packageInitializationTasks.AddTask(isMainThreadTask: true, task: EnsureComponentModelAsync);

async Task EnsureComponentModelAsync(PackageLoadTasks packageInitializationTasks, CancellationToken token)
{
if (_componentModel_doNotAccessDirectly == null)
{
_componentModel_doNotAccessDirectly = (IComponentModel?)await GetServiceAsync(typeof(SComponentModel)).ConfigureAwait(false);
Assumes.Present(_componentModel_doNotAccessDirectly);
}
var componentModel = (IComponentModel?)await GetServiceAsync(typeof(SComponentModel)).ConfigureAwait(false);
Assumes.Present(componentModel);
ComponentModel = componentModel;
}
}

protected virtual void RegisterOnAfterPackageLoadedAsyncWork(PackageLoadTasks afterPackageLoadedTasks)
{
afterPackageLoadedTasks.AddTask(isMainThreadTask: false, async (packageLoadTasks, cancellationToken) =>
{
// UIContexts can be "zombied" if UIContexts aren't supported because we're in a command line build or in other scenarios.
// Trying to await them will throw.
if (!KnownUIContexts.SolutionExistsAndFullyLoadedContext.IsZombie)
{
await KnownUIContexts.SolutionExistsAndFullyLoadedContext;

// Kick off the work, but don't block
LoadComponentsInBackgroundAfterSolutionFullyLoadedAsync(cancellationToken).ReportNonFatalErrorUnlessCancelledAsync(cancellationToken).Forget();
}
});
}

/// <summary>
Expand All @@ -85,16 +108,8 @@ protected async Task RegisterEditorFactoryAsync(IVsEditorFactory editorFactory,
ErrorHandler.ThrowOnFailure(registerEditors.RegisterEditor(editorFactory.GetType().GUID, editorFactory, out _));
}

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.
// Trying to await them will throw.
if (!KnownUIContexts.SolutionExistsAndFullyLoadedContext.IsZombie)
{
await KnownUIContexts.SolutionExistsAndFullyLoadedContext;
await LoadComponentsAsync(cancellationToken).ConfigureAwait(false);
}
}

protected abstract Task LoadComponentsAsync(CancellationToken cancellationToken);
/// <summary>
/// A method called in the background once the solution has fully loaded. Offers a place for initialization to happen after package load.
/// </summary>
protected abstract Task LoadComponentsInBackgroundAfterSolutionFullyLoadedAsync(CancellationToken cancellationToken);
}
22 changes: 7 additions & 15 deletions src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,18 @@ protected override void RegisterInitializeAsyncWork(PackageLoadTasks packageInit
{
base.RegisterInitializeAsyncWork(packageInitializationTasks);

packageInitializationTasks.AddTask(isMainThreadTask: true, task: PackageInitializationMainThreadAsync);
packageInitializationTasks.AddTask(isMainThreadTask: false, task: PackageInitializationBackgroundThreadAsync);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice.

}

private async Task PackageInitializationMainThreadAsync(PackageLoadTasks packageInitializationTasks, CancellationToken cancellationToken)
private async Task PackageInitializationBackgroundThreadAsync(PackageLoadTasks packageInitializationTasks, CancellationToken cancellationToken)
{
// We still need to ensure the RoslynPackage is loaded, since it's OnAfterPackageLoaded will hook up event handlers in RoslynPackage.LoadComponentsAsync.
// We still need to ensure the RoslynPackage is loaded, since it's OnAfterPackageLoaded will hook up event handlers in RoslynPackage.LoadComponentsInBackgroundAfterSolutionFullyLoadedAsync.
// Once that method has been replaced, then this package load can be removed.
var shell = await GetServiceAsync<SVsShell, IVsShell7>(throwOnFailure: true, cancellationToken).ConfigureAwait(true);
Assumes.Present(shell);
await shell.LoadPackageAsync(Guids.RoslynPackageId);
}
//
// Rather than triggering a package load via IVsShell (which requires a transition to the UI thread), we request an async service that is free-threaded;
// this let's us stay on the background and goes through the full background package load path.
_ = await GetServiceAsync<RoslynPackageLoadService, RoslynPackageLoadService>(throwOnFailure: true, cancellationToken).ConfigureAwait(false);

private async Task PackageInitializationBackgroundThreadAsync(PackageLoadTasks packageInitializationTasks, CancellationToken cancellationToken)
{
AddService(typeof(TLanguageService), async (_, cancellationToken, _) =>
{
// Ensure we're on the BG when creating the language service.
Expand Down Expand Up @@ -114,16 +111,11 @@ protected override void RegisterOnAfterPackageLoadedAsyncWork(PackageLoadTasks a

_objectBrowserLibraryManagerRegistered = true;
}

LoadComponentsInUIContextOnceSolutionFullyLoadedAsync(cancellationToken).Forget();
});
}

protected override async Task LoadComponentsAsync(CancellationToken cancellationToken)
protected override async Task LoadComponentsInBackgroundAfterSolutionFullyLoadedAsync(CancellationToken cancellationToken)
{
// Do the MEF loads and initialization in the BG explicitly.
await TaskScheduler.Default;

// Ensure the nuget package services are initialized. This initialization pass will only run
// once our package is loaded indirectly through a legacy COM service we proffer (like the legacy project systems
// loading us) or through something like the IVsEditorFactory or a debugger service. Right now it's fine
Expand Down
7 changes: 7 additions & 0 deletions src/VisualStudio/Core/Def/PackageRegistration.pkgdef
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,10 @@

[$RootKey$\Editors\{68b46364-d378-42f2-9e72-37d86c5f4468}\LogicalViews]
"{7651a703-06e5-11d1-8ebd-00a0c90f26ea}"=""

[$RootKey$\Services\{89e9dfce-d0f3-48c4-be76-140d5cce69ef}]
@="{6cf2e545-6109-4730-8883-cf43d7aec3e1}"
"Name"="RoslynPackageLoadService"
"IsAsyncQueryable"=dword:00000001
"IsCacheable"=dword:00000001
"IsFreeThreaded"=dword:00000001
40 changes: 8 additions & 32 deletions src/VisualStudio/Core/Def/RoslynPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using Microsoft.CodeAnalysis.EditAndContinue;
using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Remote.ProjectSystem;
using Microsoft.VisualStudio.LanguageServices.EditorConfigSettings;
using Microsoft.VisualStudio.LanguageServices.ExternalAccess.UnitTesting;
Expand All @@ -37,6 +36,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Setup;
[Guid(Guids.RoslynPackageIdString)]
[ProvideToolWindow(typeof(ValueTracking.ValueTrackingToolWindow))]
[ProvideToolWindow(typeof(StackTraceExplorerToolWindow))]
[ProvideService(typeof(RoslynPackageLoadService), IsAsyncQueryable = true, IsCacheable = true, IsFreeThreaded = true)]
internal sealed class RoslynPackage : AbstractPackage
{
private static RoslynPackage? s_lazyInstance;
Expand Down Expand Up @@ -73,28 +73,13 @@ protected override void RegisterInitializeAsyncWork(PackageLoadTasks packageInit

async Task PackageInitializationBackgroundThreadAsync(PackageLoadTasks packageInitializationTasks, CancellationToken cancellationToken)
{
AddService(typeof(RoslynPackageLoadService), (_, _, _) => Task.FromResult((object?)new RoslynPackageLoadService()), promote: true);

await RegisterEditorFactoryAsync(new SettingsEditorFactory(), cancellationToken).ConfigureAwait(true);
await ProfferServiceBrokerServicesAsync(cancellationToken).ConfigureAwait(true);
}
}

protected override void RegisterOnAfterPackageLoadedAsyncWork(PackageLoadTasks afterPackageLoadedTasks)
{
base.RegisterOnAfterPackageLoadedAsyncWork(afterPackageLoadedTasks);

afterPackageLoadedTasks.AddTask(isMainThreadTask: true, task: OnAfterPackageLoadedMainThreadAsync);

return;

Task OnAfterPackageLoadedMainThreadAsync(PackageLoadTasks afterPackageLoadedTasks, CancellationToken cancellationToken)
{
// load some services that have to be loaded in UI thread
LoadComponentsInUIContextOnceSolutionFullyLoadedAsync(cancellationToken).Forget();

return Task.CompletedTask;
}
}

private async Task ProfferServiceBrokerServicesAsync(CancellationToken cancellationToken)
{
// Proffer in-process service broker services
Expand All @@ -110,10 +95,8 @@ private async Task ProfferServiceBrokerServicesAsync(CancellationToken cancellat
(_, _, _, _) => ValueTask.FromResult<object?>(new ManagedEditAndContinueLanguageServiceBridge(this.ComponentModel.GetService<EditAndContinueLanguageService>())));
}

protected override async Task LoadComponentsAsync(CancellationToken cancellationToken)
protected override async Task LoadComponentsInBackgroundAfterSolutionFullyLoadedAsync(CancellationToken cancellationToken)
{
await TaskScheduler.Default;

// we need to load it as early as possible since we can have errors from
// package from each language very early
await this.ComponentModel.GetService<VisualStudioSuppressionFixService>().InitializeAsync(this, cancellationToken).ConfigureAwait(false);
Expand All @@ -125,7 +108,10 @@ protected override async Task LoadComponentsAsync(CancellationToken cancellation

await LoadAnalyzerNodeComponentsAsync(cancellationToken).ConfigureAwait(false);

LoadComponentsBackgroundAsync(cancellationToken).ReportNonFatalErrorUnlessCancelledAsync(cancellationToken).Forget();
await LoadStackTraceExplorerMenusAsync(cancellationToken).ConfigureAwait(false);

// Initialize keybinding reset detector
await ComponentModel.DefaultExportProvider.GetExportedValue<KeybindingReset.KeybindingResetDetector>().InitializeAsync(cancellationToken).ConfigureAwait(false);
}

// Overrides for VSSDK003 fix
Expand All @@ -151,16 +137,6 @@ protected override string GetToolWindowTitle(Type toolWindowType, int id)
protected override Task<object?> InitializeToolWindowAsync(Type toolWindowType, int id, CancellationToken cancellationToken)
=> Task.FromResult((object?)null);

private async Task LoadComponentsBackgroundAsync(CancellationToken cancellationToken)
{
await TaskScheduler.Default;

await LoadStackTraceExplorerMenusAsync(cancellationToken).ConfigureAwait(true);

// Initialize keybinding reset detector
await ComponentModel.DefaultExportProvider.GetExportedValue<KeybindingReset.KeybindingResetDetector>().InitializeAsync(cancellationToken).ConfigureAwait(true);
}

private async Task LoadStackTraceExplorerMenusAsync(CancellationToken cancellationToken)
{
// Obtain services and QueryInterface from the main thread
Expand Down
24 changes: 24 additions & 0 deletions src/VisualStudio/Core/Def/RoslynPackageLoadService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// 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.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.LanguageServices.Setup;

namespace Microsoft.VisualStudio.LanguageServices
{
/// <summary>
/// A placeholder service that exists to trigger a package load of <see cref="RoslynPackage"/>. The only way to trigger an explicit load
/// is via <see cref="Microsoft.VisualStudio.Shell.Interop.IVsShell.LoadPackage"/>, but calling that API requires a transition of the UI thread first.
/// Requesting a service that is free-threaded instead can trigger the package load from the background thread in the first place.
/// </summary>
[Guid("89e9dfce-d0f3-48c4-be76-140d5cce69ef")]
internal sealed class RoslynPackageLoadService
{
}
}
Loading