| File: Evaluation\SimpleProjectRootElementCache.cs | Web Access |
| Project: src\msbuild\src\Build\Microsoft.Build.csproj (Microsoft.Build) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Concurrent; using Microsoft.Build.Construction; using Microsoft.Build.Shared; #nullable disable namespace Microsoft.Build.Evaluation { /// <summary> /// Maintains a cache of all loaded ProjectRootElement instances for design time purposes. /// /// This avoids the LRU cache and class-wide lock used within ProjectRootElementCache and replaces these mechanisms /// with a single ConcurrentDictionary as a tradeoff for increased performance when evaluating projects in parallel. /// As a tradeoff, this implementation uses more memory, and is not intended for use when the cache needs to be /// long-lived e.g. within Visual Studio. /// /// SimpleProjectRootElementCache is not currently intended for use outside of evaluation. Several code paths /// executed within a full build take a hard dependency on the strong/weak reference behavior used within /// ProjectRootElementCache, and further investigation is required to determine the best way to hide these behind /// an abstraction. As such, any method unused by evaluation will throw NotImplementedException. /// </summary> internal class SimpleProjectRootElementCache : ProjectRootElementCacheBase { private readonly ConcurrentDictionary<string, ProjectRootElement> _cache; internal SimpleProjectRootElementCache() { _cache = new ConcurrentDictionary<string, ProjectRootElement>(StringComparer.OrdinalIgnoreCase); LoadProjectsReadOnly = true; } internal override ProjectRootElement Get( string projectFile, OpenProjectRootElement loadProjectRootElement, bool isExplicitlyLoaded, bool? preserveFormatting) { // Should already have been canonicalized ErrorUtilities.VerifyThrowInternalRooted(projectFile); return loadProjectRootElement == null ? GetFromCache(projectFile) : GetFromOrAddToCache(projectFile, loadProjectRootElement); } private ProjectRootElement GetFromCache(string projectFile) { if (_cache.TryGetValue(projectFile, out ProjectRootElement projectRootElement)) { return projectRootElement; } return null; } private ProjectRootElement GetFromOrAddToCache(string projectFile, OpenProjectRootElement loadFunc) { return _cache.GetOrAdd(projectFile, key => { ProjectRootElement rootElement = loadFunc(key, this); Assumed.NotNull(rootElement); Assumed.Equal(rootElement.FullPath, key, StringComparison.OrdinalIgnoreCase, $"Got project back with incorrect path. Expected path: {key}, received path: {rootElement.FullPath}."); AddEntry(rootElement); Assumed.True(_cache.TryGetValue(key, out _), "Project should have been added into cache and boosted"); return rootElement; }); } internal override void AddEntry(ProjectRootElement projectRootElement) { if (_cache.TryAdd(projectRootElement.FullPath, projectRootElement)) { RaiseProjectRootElementAddedToCacheEvent(projectRootElement); } } internal override void RenameEntry(string oldFullPath, ProjectRootElement projectRootElement) { throw new NotImplementedException(); } internal override ProjectRootElement TryGet(string projectFile) { return TryGet(projectFile, null); } internal override ProjectRootElement TryGet(string projectFile, bool? preserveFormatting) { return Get( projectFile, null, false, preserveFormatting); } internal override void DiscardStrongReferences() { } internal override void Clear() { _cache.Clear(); } internal override void DiscardImplicitReferences() { throw new NotImplementedException(); } internal override void DiscardAnyWeakReference(ProjectRootElement projectRootElement) { ArgumentNullException.ThrowIfNull(projectRootElement); // A PRE may be unnamed if it was only used in memory. if (projectRootElement.FullPath != null) { _cache.TryRemove(projectRootElement.FullPath, out _); } } internal override void OnProjectRootElementDirtied(ProjectRootElement sender, ProjectXmlChangedEventArgs e) { throw new NotImplementedException(); } internal override void OnProjectDirtied(Project sender, ProjectChangedEventArgs e) { throw new NotImplementedException(); } } }