/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/VisualStudio/Core/Def/SymbolSearch/AbstractDelayStartedService.cs
113 строк
5 KB
Tomáš Matoušek
Move async utils to Microsoft.CodeAnalysis.Threading source package (#76937)
05 фев 2025, 23:07
Не верифицирован
05 фев 2025, 23:07
dead7da
Код
Авторство
О чём код?
// 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.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.SymbolSearch; /// <summary> /// Base type for services that we want to delay running until certain criteria is met. For example, we don't want /// to run the <see cref="VisualStudioSymbolSearchService"/> core codepath if the user has not enabled the features /// that need it. That helps us avoid loading dlls unnecessarily and bloating the VS memory space. /// </summary> internal abstract class AbstractDelayStartedService { protected readonly IThreadingContext ThreadingContext; private readonly IGlobalOptionService _globalOptions; protected readonly VisualStudioWorkspaceImpl Workspace; /// <summary> /// The set of languages that have loaded that care about this service. If one language loads and has an /// appropriate <see cref="_perLanguageOptions"/> also enabled, then this service will start working. /// </summary> private readonly ConcurrentSet<string> _registeredLanguages = []; /// <summary> /// Option that controls if this service is enabled or not (regardless of language). /// </summary> private readonly Option2<bool> _featureEnabledOption; /// <summary> /// Options that control if this service is enabled or not for a particular language. /// </summary> private readonly ImmutableArray<PerLanguageOption2<bool>> _perLanguageOptions; protected CancellationToken DisposalToken => ThreadingContext.DisposalToken; private readonly AsyncBatchingWorkQueue _optionChangedWorkQueue; private bool _enabled = false; protected AbstractDelayStartedService( IThreadingContext threadingContext, IGlobalOptionService globalOptions, VisualStudioWorkspaceImpl workspace, IAsynchronousOperationListenerProvider listenerProvider, Option2<bool> featureEnabledOption, ImmutableArray<PerLanguageOption2<bool>> perLanguageOptions) { ThreadingContext = threadingContext; _globalOptions = globalOptions; Workspace = workspace; _featureEnabledOption = featureEnabledOption; _perLanguageOptions = perLanguageOptions; _optionChangedWorkQueue = new AsyncBatchingWorkQueue( DelayTimeSpan.Medium, ProcessOptionChangesAsync, listenerProvider.GetListener(FeatureAttribute.Workspace), this.DisposalToken); _globalOptions.AddOptionChangedHandler(this, OnOptionChanged); } protected abstract Task EnableServiceAsync(CancellationToken cancellationToken); public void RegisterLanguage(string language) { _registeredLanguages.Add(language); _optionChangedWorkQueue.AddWork(); } private void OnOptionChanged(object sender, object target, OptionChangedEventArgs e) => _optionChangedWorkQueue.AddWork(); private async ValueTask ProcessOptionChangesAsync(CancellationToken arg) { // If we're already enabled, nothing to do. if (_enabled) return; // If feature is totally disabled. Do nothing. if (!_globalOptions.GetOption(_featureEnabledOption)) return; // If feature isn't enabled for any registered language, do nothing. var languageEnabled = _registeredLanguages.Any(lang => _perLanguageOptions.Any(static (option, arg) => arg.self._globalOptions.GetOption(option, arg.lang), (self: this, lang))); if (!languageEnabled) return; // We were enabled for some language. Kick off the work for this service now. Since we're now enabled, we // no longer need to listen for option changes. _enabled = true; _globalOptions.RemoveOptionChangedHandler(this, OnOptionChanged); // Don't both kicking off delay-started services prior to the actual workspace being fully loaded. We don't // want them using CPU/memory in the BG while we're loading things for the user. var statusService = this.Workspace.Services.GetRequiredService<IWorkspaceStatusService>(); await statusService.WaitUntilFullyLoadedAsync(this.DisposalToken).ConfigureAwait(false); await this.EnableServiceAsync(this.DisposalToken).ConfigureAwait(false); } }