Skip to content

Remove RoslynServiceExtensions.GetServiceAsync #77799

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

Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 0 additions & 2 deletions eng/config/BannedSymbols.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ M:Microsoft.CodeAnalysis.Document.GetOptionsAsync(System.Threading.CancellationT
T:Microsoft.CodeAnalysis.Options.DocumentOptionSet; Use AnalyzerConfigOptions instead
M:Microsoft.VisualStudio.Shell.ServiceExtensions.GetService``2(System.IServiceProvider); Use RoslynServiceExtensions instead. This extension internally relies on ThreadHelper, which is incompatible with testing.
M:Microsoft.VisualStudio.Shell.ServiceExtensions.GetService``2(System.IServiceProvider,System.Boolean); Use RoslynServiceExtensions instead. This extension internally relies on ThreadHelper, which is incompatible with testing
Comment on lines 27 to 28
Copy link
Member

Choose a reason for hiding this comment

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

Should these be banned too, or the message updated for it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I didn't look into these methods, so I'd rather not remove or change their message

M:Microsoft.VisualStudio.Shell.ServiceExtensions.GetServiceAsync``2(Microsoft.VisualStudio.Shell.IAsyncServiceProvider); Use RoslynServiceExtensions instead. This extension internally relies on ThreadHelper, which is incompatible with testing
M:Microsoft.VisualStudio.Shell.ServiceExtensions.GetServiceAsync``2(Microsoft.VisualStudio.Shell.IAsyncServiceProvider,System.Boolean); Use RoslynServiceExtensions instead. This extension internally relies on ThreadHelper, which is incompatible with testing
P:Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory; Use IThreadingContext.JoinableTaskFactory instead.
M:Microsoft.CodeAnalysis.Formatting.Formatter.FormatAsync(Microsoft.CodeAnalysis.Document,Microsoft.CodeAnalysis.Options.OptionSet,System.Threading.CancellationToken); Use internal overload instead
M:Microsoft.CodeAnalysis.Formatting.Formatter.FormatAsync(Microsoft.CodeAnalysis.Document,Microsoft.CodeAnalysis.Text.TextSpan,Microsoft.CodeAnalysis.Options.OptionSet,System.Threading.CancellationToken); Use overload with SyntaxFormattingOptions instead
Expand Down
91 changes: 0 additions & 91 deletions src/EditorFeatures/Core.Wpf/Utilities/RoslynServiceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Threading;

namespace Microsoft.VisualStudio.Shell
Expand All @@ -25,12 +23,6 @@ public static TInterface GetServiceOnMainThread<TService, TInterface>(this IServ
return @interface;
}

/// <summary>
/// Returns the specified service type from the service.
/// </summary>
public static TServiceType GetServiceOnMainThread<TServiceType>(this IServiceProvider serviceProvider) where TServiceType : class
=> serviceProvider.GetServiceOnMainThread<TServiceType, TServiceType>();

/// <summary>
/// Gets a service interface from a service provider.
/// </summary>
Expand Down Expand Up @@ -99,88 +91,5 @@ public static TInterface GetService<TService, TInterface>(
return @interface;
});
}

/// <summary>
/// Gets a service interface from a service provider asynchronously.
/// </summary>
/// <typeparam name="TService">The service type</typeparam>
/// <typeparam name="TInterface">The interface type</typeparam>
/// <param name="asyncServiceProvider">The async service provider</param>
/// <returns>The requested service interface. Never <see langword="null"/>.</returns>
/// <exception cref="ServiceUnavailableException">
/// Either the service could not be acquired, or the service does not support
/// the requested interface.
/// </exception>
public static Task<TInterface> GetServiceAsync<TService, TInterface>(
this IAsyncServiceProvider asyncServiceProvider,
JoinableTaskFactory joinableTaskFactory)
where TInterface : class
{
return GetServiceAsync<TService, TInterface>(asyncServiceProvider, joinableTaskFactory, throwOnFailure: true)!;
}

/// <summary>
/// Gets a service interface from a service provider asynchronously.
/// </summary>
/// <typeparam name="TService">The service type</typeparam>
/// <typeparam name="TInterface">The interface type</typeparam>
/// <param name="asyncServiceProvider">The async service provider</param>
/// <param name="throwOnFailure">
/// Determines how a failure to get the requested service interface is handled. If <see langword="true"/>, an
/// exception is thrown; if <see langword="false"/>, <see langword="null"/> is returned.
/// </param>
/// <returns>The requested service interface, if it could be obtained; otherwise <see langword="null"/> if
/// <paramref name="throwOnFailure"/> is <see langword="false"/>.</returns>
/// <exception cref="ServiceUnavailableException">
/// Either the service could not be acquired, or the service does not support
/// the requested interface.
/// </exception>
public static async Task<TInterface?> GetServiceAsync<TService, TInterface>(
this IAsyncServiceProvider asyncServiceProvider,
JoinableTaskFactory joinableTaskFactory,
bool throwOnFailure)
where TInterface : class
{
Requires.NotNull(asyncServiceProvider, nameof(asyncServiceProvider));
object? service;

// Prefer IAsyncServiceProvider2 so that any original exceptions can be captured and included as an inner
// exception to the one that we throw.
if (throwOnFailure && asyncServiceProvider is IAsyncServiceProvider2 asyncServiceProvider2)
{
try
{
service = await asyncServiceProvider2.GetServiceAsync(typeof(TService), swallowExceptions: false).ConfigureAwait(true);
}
catch (Exception ex)
{
throw new ServiceUnavailableException(typeof(TService), ex);
}
}
else
{
service = await asyncServiceProvider.GetServiceAsync(typeof(TService)).ConfigureAwait(true);
}

if (service == null)
{
if (throwOnFailure)
throw new ServiceUnavailableException(typeof(TService));

return null;
}

await joinableTaskFactory.SwitchToMainThreadAsync();

if (service is not TInterface @interface)
{
if (throwOnFailure)
throw new ServiceUnavailableException(typeof(TInterface));

return null;
}

return @interface;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public StubVsServiceExporter(
[Import(typeof(SAsyncServiceProvider))] IAsyncServiceProvider2 asyncServiceProvider,
Copy link
Member

Choose a reason for hiding this comment

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

@davkean's bug specifically calls out IAsyncServiceProvider3, do we need to move to those newer interface here or are we good? His comment makes me think all the methods will eventually call the right thing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My understanding is that the implementations on each of the interfaces will do the right thing, and that there is no need to explicitly use the "3" interface.

JoinableTaskContext joinableTaskContext)
{
_serviceGetter = new AsyncLazy<TInterface>(() => asyncServiceProvider.GetServiceAsync<TService, TInterface>(joinableTaskContext.Factory, throwOnFailure: true)!, joinableTaskContext.Factory);
_serviceGetter = new AsyncLazy<TInterface>(() => asyncServiceProvider.GetServiceAsync<TService, TInterface>(throwOnFailure: true, CancellationToken.None)!, joinableTaskContext.Factory);
}

/// <inheritdoc />
Expand Down
4 changes: 2 additions & 2 deletions src/VisualStudio/Core/Def/ColorSchemes/ColorSchemeApplier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@ public void RegisterInitializationWork(PackageLoadTasks packageInitializationTas

private async Task AfterPackageLoadedBackgroundThreadAsync(PackageLoadTasks afterPackageLoadedTasks, CancellationToken cancellationToken)
{
var settingsManager = await _asyncServiceProvider.GetServiceAsync<SVsSettingsPersistenceManager, ISettingsManager>(_threadingContext.JoinableTaskFactory).ConfigureAwait(false);
var settingsManager = await _asyncServiceProvider.GetServiceAsync<SVsSettingsPersistenceManager, ISettingsManager>(throwOnFailure: true, cancellationToken).ConfigureAwait(false);

// We need to update the theme whenever the Editor Color Scheme setting changes.
settingsManager.GetSubset(ColorSchemeOptionsStorage.ColorSchemeSettingKey).SettingChangedAsync += ColorSchemeChangedAsync;
settingsManager!.GetSubset(ColorSchemeOptionsStorage.ColorSchemeSettingKey).SettingChangedAsync += ColorSchemeChangedAsync;

// Try to migrate the `useEnhancedColorsSetting` to the new `ColorSchemeName` setting.
_settings.MigrateToColorSchemeSetting();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public async Task InitializeAsync(IAsyncServiceProvider serviceProvider, Cancell
_serviceProvider = (IServiceProvider)serviceProvider;

// Hook up the "Run Code Analysis" menu command for CPS based managed projects.
var menuCommandService = await serviceProvider.GetServiceAsync<IMenuCommandService, IMenuCommandService>(_threadingContext.JoinableTaskFactory, throwOnFailure: false).ConfigureAwait(false);
var menuCommandService = await serviceProvider.GetServiceAsync<IMenuCommandService, IMenuCommandService>(throwOnFailure: false, cancellationToken).ConfigureAwait(false);
if (menuCommandService != null)
{
await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public async Task RegisterAsync(IAsyncServiceProvider serviceProvider, Cancellat
{
if (!_eventsHookedUp)
{
var trackProjectDocuments = await serviceProvider.GetServiceAsync<SVsTrackProjectDocuments, IVsTrackProjectDocuments2>(_threadingContext.JoinableTaskFactory).ConfigureAwait(false);
var trackProjectDocuments = await serviceProvider.GetServiceAsync<SVsTrackProjectDocuments, IVsTrackProjectDocuments2>(throwOnFailure: true, cancellationToken).ConfigureAwait(false);
await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);

if (!_eventsHookedUp)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -924,11 +924,11 @@ private bool TryAddEditorConfigToSolutionItems(
return false;

// All checks pass, so let's treat this special.
var dte = _threadingContext.JoinableTaskFactory.Run(() => _asyncServiceProvider.GetServiceAsync<SDTE, EnvDTE.DTE>(_threadingContext.JoinableTaskFactory));
var dte = _threadingContext.JoinableTaskFactory.Run(() => _asyncServiceProvider.GetServiceAsync<SDTE, EnvDTE.DTE>(throwOnFailure: true, _threadingContext.DisposalToken));

const string SolutionItemsFolderName = "Solution Items";

var projects = dte.Solution.Projects.OfType<EnvDTE.Project>();
var projects = dte!.Solution.Projects.OfType<EnvDTE.Project>();
var solutionItemsFolder = projects.FirstOrDefault(static p => p.Kind == EnvDTE.Constants.vsProjectKindSolutionItems && p.Name == SolutionItemsFolderName);

if (solutionItemsFolder != null)
Expand Down
2 changes: 1 addition & 1 deletion src/VisualStudio/Core/Def/RoslynPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ protected override async Task LoadComponentsAsync(CancellationToken cancellation

// 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).ConfigureAwait(false);
await this.ComponentModel.GetService<VisualStudioSuppressionFixService>().InitializeAsync(this, cancellationToken).ConfigureAwait(false);
await this.ComponentModel.GetService<VisualStudioDiagnosticListSuppressionStateService>().InitializeAsync(this, cancellationToken).ConfigureAwait(false);

await this.ComponentModel.GetService<IVisualStudioDiagnosticAnalyzerService>().InitializeAsync(this, cancellationToken).ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public async Task InitializeAsync(IAsyncServiceProvider serviceProvider, Cancell
_serviceProvider = (IServiceProvider)serviceProvider;

// Hook up the "Remove Unused References" menu command for CPS based managed projects.
var menuCommandService = await serviceProvider.GetServiceAsync<IMenuCommandService, IMenuCommandService>(_threadingContext.JoinableTaskFactory, throwOnFailure: false).ConfigureAwait(false);
var menuCommandService = await serviceProvider.GetServiceAsync<IMenuCommandService, IMenuCommandService>(throwOnFailure: false, cancellationToken).ConfigureAwait(false);
if (menuCommandService != null)
{
await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ public VisualStudioDiagnosticListSuppressionStateService(

public async Task InitializeAsync(IAsyncServiceProvider serviceProvider, CancellationToken cancellationToken)
{
_shellService = await serviceProvider.GetServiceAsync<SVsUIShell, IVsUIShell>(_threadingContext.JoinableTaskFactory).ConfigureAwait(false);
var errorList = await serviceProvider.GetServiceAsync<SVsErrorList, IErrorList>(_threadingContext.JoinableTaskFactory, throwOnFailure: false).ConfigureAwait(false);
_shellService = await serviceProvider.GetServiceAsync<SVsUIShell, IVsUIShell>(throwOnFailure: false, cancellationToken).ConfigureAwait(false);
var errorList = await serviceProvider.GetServiceAsync<SVsErrorList, IErrorList>(throwOnFailure: false, cancellationToken).ConfigureAwait(false);
_tableControl = errorList?.TableControl;

await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ internal sealed class VisualStudioSuppressionFixService(

private IWpfTableControl? _tableControl;

public async Task InitializeAsync(IAsyncServiceProvider serviceProvider)
public async Task InitializeAsync(IAsyncServiceProvider serviceProvider, CancellationToken cancellationToken)
{
var errorList = await serviceProvider.GetServiceAsync<SVsErrorList, IErrorList>(_threadingContext.JoinableTaskFactory, throwOnFailure: false).ConfigureAwait(false);
var errorList = await serviceProvider.GetServiceAsync<SVsErrorList, IErrorList>(throwOnFailure: false, cancellationToken).ConfigureAwait(false);
_tableControl = errorList?.TableControl;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public async Task InitializeAsync(IAsyncServiceProvider serviceProvider, Cancell
_serviceProvider = (IServiceProvider)serviceProvider;

// Hook up the "Remove Unused References" menu command for CPS based managed projects.
var menuCommandService = await serviceProvider.GetServiceAsync<IMenuCommandService, IMenuCommandService>(_threadingContext.JoinableTaskFactory, throwOnFailure: false).ConfigureAwait(false);
var menuCommandService = await serviceProvider.GetServiceAsync<IMenuCommandService, IMenuCommandService>(throwOnFailure: false, cancellationToken).ConfigureAwait(false);
if (menuCommandService != null)
{
await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ private void EnsureSubscribedToSolutionEvents()
{
Task.Run(async () =>
{
var shellService = await _serviceProvider.GetServiceAsync<SVsSolution, IVsSolution>(_threadingContext.JoinableTaskFactory).ConfigureAwait(true);
var shellService = await _serviceProvider.GetServiceAsync<SVsSolution, IVsSolution>(throwOnFailure: true, _threadingContext.DisposalToken).ConfigureAwait(true);
await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(_threadingContext.DisposalToken);
shellService.AdviseSolutionEvents(this, out _);
shellService!.AdviseSolutionEvents(this, out _);
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we should be putting the ! on the fetching so that way the nullable suppression is closer to the place where the throwOnFailure: true is. Or it's unfortunate if we can't get that nullable annotated in some way to make this simpler.

Copy link
Contributor Author

@ToddGrun ToddGrun Mar 25, 2025

Choose a reason for hiding this comment

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

Looks like there is a version of GetServiceAsync that only takes in a CancellationToken and assumes throwOnFailure is true and has null annotated the return values. Switched over to use that when possible.

}, _threadingContext.DisposalToken);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public Service(IAsyncServiceProvider2 serviceProvider, IThreadingContext threadi
await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(alwaysYield: true, _threadingContext.DisposalToken);

// Make sure the HubClient package is loaded, since we rely on it for proffered OOP services
var shell = await _serviceProvider.GetServiceAsync<SVsShell, IVsShell7>(_threadingContext.JoinableTaskFactory).ConfigureAwait(true);
var shell = await _serviceProvider.GetServiceAsync<SVsShell, IVsShell7>(throwOnFailure: true, _threadingContext.DisposalToken).ConfigureAwait(true);
Assumes.Present(shell);

await shell.LoadPackageAsync(Guids.GlobalHubClientPackageGuid);
Expand All @@ -90,7 +90,7 @@ public Service(IAsyncServiceProvider2 serviceProvider, IThreadingContext threadi
using var asyncToken = listener.BeginAsyncOperation("StatusChanged_EventSubscription");

await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(alwaysYield: true, _threadingContext.DisposalToken);
var service = await serviceProvider.GetServiceAsync<SVsOperationProgress, IVsOperationProgressStatusService>(_threadingContext.JoinableTaskFactory, throwOnFailure: false).ConfigureAwait(true);
var service = await serviceProvider.GetServiceAsync<SVsOperationProgress, IVsOperationProgressStatusService>(throwOnFailure: false, _threadingContext.DisposalToken).ConfigureAwait(true);
if (service is null)
return null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,13 @@ public async Task<IWorkspaceProjectContext> CreateProjectContextAsync(Guid id, s
{
await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);

var shell = await _serviceProvider.GetServiceAsync<SVsShell, IVsShell7>(_threadingContext.JoinableTaskFactory).ConfigureAwait(true);
var shell = await _serviceProvider.GetServiceAsync<SVsShell, IVsShell7>(throwOnFailure: true, cancellationToken).ConfigureAwait(true);

// Force the F# package to load; this is necessary because the F# package listens to WorkspaceChanged to
// set up some items, and the F# project system doesn't guarantee that the F# package has been loaded itself
// so we're caught in the middle doing this.
var packageId = Guids.FSharpPackageId;
await shell.LoadPackageAsync(ref packageId);
await shell!.LoadPackageAsync(ref packageId);

await TaskScheduler.Default;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ internal sealed class AnalyzerItemsTracker(IThreadingContext threadingContext) :

public async Task RegisterAsync(IAsyncServiceProvider serviceProvider, CancellationToken cancellationToken)
{
_vsMonitorSelection ??= await serviceProvider.GetServiceAsync<SVsShellMonitorSelection, IVsMonitorSelection>(_threadingContext.JoinableTaskFactory, throwOnFailure: false).ConfigureAwait(false);
_vsMonitorSelection ??= await serviceProvider.GetServiceAsync<SVsShellMonitorSelection, IVsMonitorSelection>(throwOnFailure: false, cancellationToken).ConfigureAwait(false);
await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
_vsMonitorSelection?.AdviseSelectionEvents(this, out _selectionEventsCookie);
}
Expand Down
Loading