/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace_SolutionCaching.cs
139 строк
7 KB
Cyrus Najmabadi
Remove unused usings
18 июн 2025, 22:17
18 июн 2025, 22:17
481c36c
Код
Авторство
О чём код?
// 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.Threading; using System.Threading.Tasks; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; internal sealed partial class RemoteWorkspace { /// <summary> /// The last solution for the primary branch fetched from the client. Cached as it's very common to have a /// flurry of requests for the same checksum that don't run concurrently. Only read/write while holding <see /// cref="_gate"/>. /// </summary> private (Checksum checksum, Solution solution) _lastRequestedPrimaryBranchSolution; /// <summary> /// Cache of last N solutions requested by a service. Cached as it's very common to have a flurry of requests /// for the same few checksum that don't run concurrently. Only read/write while holding <see cref="_gate"/>. /// </summary> private readonly RemoteSolutionCache<Checksum, Solution> _lastRequestedAnyBranchSolutions = new(); /// <summary> /// Mapping from solution-checksum to the solution computed for it. This is used so that we can hold a solution /// around as long as the checksum for it is being used in service of some feature operation (e.g. /// classification). As long as we're holding onto it, concurrent feature requests for the same solution /// checksum can share the computation of that particular solution and avoid duplicated concurrent work. Only /// read/write while holding <see cref="_gate"/>. /// </summary> private readonly Dictionary<Checksum, InFlightSolution> _solutionChecksumToSolution = []; /// <summary> /// Deliberately not cancellable. This code must always run fully to completion. /// </summary> private InFlightSolution GetOrCreateSolutionAndAddInFlightCount_NoLock( AssetProvider assetProvider, Checksum solutionChecksum, bool updatePrimaryBranch) { Contract.ThrowIfFalse(_gate.CurrentCount == 0); CheckCacheInvariants_NoLock(); var solution = GetOrCreateSolutionAndAddInFlightCount_NoLock(); // The solution must now have a valid in-flight-count. Contract.ThrowIfTrue(solution.InFlightCount < 1); // We may be getting back a solution that only was computing a non-primary branch. If we were asked // to compute the primary branch as well, let it know so it can start that now. if (updatePrimaryBranch) { solution.TryKickOffPrimaryBranchWork_NoLock((disconnectedSolution, cancellationToken) => this.UpdateWorkspaceCurrentSolutionAsync(disconnectedSolution, cancellationToken)); } CheckCacheInvariants_NoLock(); return solution; InFlightSolution GetOrCreateSolutionAndAddInFlightCount_NoLock() { Contract.ThrowIfFalse(_gate.CurrentCount == 0); if (_solutionChecksumToSolution.TryGetValue(solutionChecksum, out var solution)) { // The cached solution must have a valid in-flight-count Contract.ThrowIfTrue(solution.InFlightCount < 1); // Increase the count as our caller now is keeping this solution in-flight solution.IncrementInFlightCount_NoLock(); Contract.ThrowIfTrue(solution.InFlightCount < 2); return solution; } // See if we're being asked for a checksum we already have cached a solution for. Safe to read directly // as we're holding _gate. var cachedSolution = _lastRequestedPrimaryBranchSolution.checksum == solutionChecksum ? _lastRequestedPrimaryBranchSolution.solution : _lastRequestedAnyBranchSolutions.Find(solutionChecksum); // We're the first call that is asking about this checksum. Kick off async computation to compute it // (or use an existing cached value we already have). Start with an in-flight-count of 1 to represent // our caller. solution = new InFlightSolution( this, solutionChecksum, async cancellationToken => cachedSolution ?? await ComputeDisconnectedSolutionAsync(assetProvider, solutionChecksum, cancellationToken).ConfigureAwait(false)); Contract.ThrowIfFalse(solution.InFlightCount == 1); _solutionChecksumToSolution.Add(solutionChecksum, solution); return solution; } } private void CheckCacheInvariants_NoLock() { Contract.ThrowIfFalse(_gate.CurrentCount == 0); foreach (var (solutionChecksum, solution) in _solutionChecksumToSolution) { // Anything in this dictionary is currently in flight with an existing request. So it must have an // in-flight-count of at least 1. Note: this in-flight-request may be an actual request that has come // in from the client. Or it can be a virtual one we've created through _lastAnyBranchSolution or // _lastPrimaryBranchSolution Contract.ThrowIfTrue(solution.InFlightCount < 1); Contract.ThrowIfTrue(solutionChecksum != solution.SolutionChecksum); } } /// <summary> /// Gets all the solution instances this remote workspace knows about because of the primary solution or any /// in-flight operations. /// </summary> public async ValueTask AddPinnedSolutionsAsync(HashSet<Solution> solutions, CancellationToken cancellationToken) { using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { // Ensure everything in the workspace's current solution is pinned. We def don't want any of its data // dropped from the checksum->asset cache. solutions.Add(this.CurrentSolution); // Also the data for the last 'current solution' this workspace had that we actually got an OOP request // for. this is commonly the same as CurrentSolution, but technically could be slightly behind if the // primary solution just got updated. solutions.AddIfNotNull(_lastRequestedPrimaryBranchSolution.solution); // Also add the last few forked solutions we were asked about. As with the above solutions, there's a // reasonable chance it will refer to data needed by future oop calls. _lastRequestedAnyBranchSolutions.AddAllTo(solutions); } } }