File: Workspace\Solution\Solution_SemanticModelCaching.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// 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 Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis;
 
public partial class Solution
{
    /// <summary>
    /// Strongly held reference to the semantic models for the active document (and its related documents linked into
    /// other projects).  By strongly holding onto them, we ensure that they won't be GC'ed between feature requests
    /// from multiple features that care about it.  As the active document has the most features running on it
    /// continuously, we definitely do not want to drop this.  Note: this cached value is only to help with performance.
    /// Not with correctness.  Importantly, the concept of 'active document' is itself fundamentally racy.  That's ok
    /// though as we simply want to settle on these semantic models settling into a stable state over time.  We don't
    /// need to be perfect about it.
    /// </summary>
    private ImmutableArray<(DocumentId documentId, SemanticModel semanticModel)> _activeSemanticModels = [];
 
    internal void OnSemanticModelObtained(
        DocumentId documentId, SemanticModel semanticModel)
    {
        var service = this.Services.GetRequiredService<IDocumentTrackingService>();
 
        // Operate on a local reference to the immutable array to ensure a consistent view of it.
        var localArray = _activeSemanticModels;
 
        // No need to do anything if we're already caching this pair.
        if (localArray.Contains((documentId, semanticModel)))
            return;
 
        var activeDocumentId = service.TryGetActiveDocument();
        var relatedDocumentIds = activeDocumentId is null ? [] : this.GetRelatedDocumentIds(activeDocumentId);
 
        // Remove any cached values for documents that are no longer the active document.
        localArray = localArray.RemoveAll(
            tuple => !relatedDocumentIds.Contains(tuple.documentId));
 
        // Now cache this doc/semantic model pair if it's in the related document set.
        if (relatedDocumentIds.Contains(documentId))
            localArray = localArray.Add((documentId, semanticModel));
 
        // Note: this code is racy. We could have two threads executing the code above, while only one thread will win
        // here.  We accept that as this code is just intended to help just by making some strong references to semantic
        // models to prevent them from being GC'ed.  We don't need to be perfect about it.
        _activeSemanticModels = localArray;
    }
}